asciidoctor / asciidoctorj

:coffee: Java bindings for Asciidoctor. Asciidoctor on the JVM!
http://asciidoctor.org
Apache License 2.0
617 stars 172 forks source link

BlockProcessor causes exception with missing contexts option #446

Open ygra opened 8 years ago

ygra commented 8 years ago

I've tried writing a Block processor in AsciidoctorJ, more or less starting from the YellBlock example. However, I constantly got an exception:

Exception in thread "main" org.asciidoctor.internal.AsciidoctorCoreException: org.jruby.exceptions.RaiseException: (NoMethodError) asciidoctor: FAILED: <stdin>: Failed to load AsciiDoc document - undefined method `include?' for nil:NilClass
    at org.asciidoctor.internal.JRubyAsciidoctor.render(JRubyAsciidoctor.java:314)
    at org.asciidoctor.internal.JRubyAsciidoctor.render(JRubyAsciidoctor.java:422)
    at org.asciidoctor.internal.JRubyAsciidoctor.convert(JRubyAsciidoctor.java:524)
    at Main.main(Main.java:10)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: org.jruby.exceptions.RaiseException: (NoMethodError) asciidoctor: FAILED: <stdin>: Failed to load AsciiDoc document - undefined method `include?' for nil:NilClass
    at RUBY.registered_for_block?(C:/Users/roessel/.m2/repository/org/asciidoctor/asciidoctorj/1.5.4/asciidoctorj-1.5.4.jar!/gems/asciidoctor-1.5.4/lib/asciidoctor/extensions.rb:907)
    at RUBY.next_block(C:/Users/roessel/.m2/repository/org/asciidoctor/asciidoctorj/1.5.4/asciidoctorj-1.5.4.jar!/gems/asciidoctor-1.5.4/lib/asciidoctor/parser.rb:689)
    at RUBY.next_section(C:/Users/roessel/.m2/repository/org/asciidoctor/asciidoctorj/1.5.4/asciidoctorj-1.5.4.jar!/gems/asciidoctor-1.5.4/lib/asciidoctor/parser.rb:315)
    at RUBY.parse(C:/Users/roessel/.m2/repository/org/asciidoctor/asciidoctorj/1.5.4/asciidoctorj-1.5.4.jar!/gems/asciidoctor-1.5.4/lib/asciidoctor/parser.rb:67)
    at RUBY.parse(C:/Users/roessel/.m2/repository/org/asciidoctor/asciidoctorj/1.5.4/asciidoctorj-1.5.4.jar!/gems/asciidoctor-1.5.4/lib/asciidoctor/document.rb:471)
    at RUBY.load(C:/Users/roessel/.m2/repository/org/asciidoctor/asciidoctorj/1.5.4/asciidoctorj-1.5.4.jar!/gems/asciidoctor-1.5.4/lib/asciidoctor.rb:1344)
    at RUBY.convert(C:/Users/roessel/.m2/repository/org/asciidoctor/asciidoctorj/1.5.4/asciidoctorj-1.5.4.jar!/gems/asciidoctor-1.5.4/lib/asciidoctor.rb:1462)
    at RUBY.convert(<script>:72)
    at org.jruby.gen.InterfaceImpl865194084.convert(org/jruby/gen/InterfaceImpl865194084.gen:13)

(At least 1.5.4 is more helpful by including the Ruby stacktrace). This seems to stem from the fact that there is no entry with key :contexts in the extension options. The relevant Ruby portion for this is:

    # Public: Checks whether any {BlockProcessor} extensions are registered to
    # handle the specified block name appearing on the specified context.
    #
    # Returns the [Extension] proxy object for the BlockProcessor that matches
    # the block name and context or false if no match is found.
    def registered_for_block? name, context
      if (ext = @block_extensions[name.to_sym])
        (ext.config[:contexts].include? context) ? ext : false
      else
        false
      end
    end

The exception happens in the if block.

This essentially means that the example as given in the docs does not work (and the exceptions are/were less than helpful in figuring out what to change).

robertpanzer commented 8 years ago

Hi,

agreed, the documentation is not complete yet. What is missing is passing the context via the config: https://github.com/asciidoctor/asciidoctorj/blob/master/asciidoctorj-core/src/test/java/org/asciidoctor/extension/WhenJavaExtensionIsRegistered.java#L653-L657

        Map<String, Object> config = new HashMap<String, Object>();
        config.put("contexts", Arrays.asList(":paragraph"));
        config.put("content_model", ":simple");
        YellBlock yellBlock = new YellBlock("yell", config);
        javaExtensionRegistry.block(yellBlock);

Please note that the extensions will get a major overhaul with AsciidoctorJ 1.6.0 and this is already documented there: https://github.com/asciidoctor/asciidoctorj/blob/asciidoctorj-1.6.0/docs/integrator-guide.adoc#block-processors While annotation based configuration is not available on the currently published version of AsciidoctorJ and the method signatures are a bit different I think it still shows what configuration parameters are necessary for an extension.

mojavelinux commented 8 years ago

I'll add my recommendation that anyone writing extensions for AsciidoctorJ should be using AsciidoctorJ 1.6.0. The API has been refactored and documented, so it's going to be a much smoother experience. It also allows us to more easily accept feedback since development is happening on the 1.6.0 branch.

ygra commented 8 years ago

@mojavelinux So essentially, if I'm starting out right now to build a toolchain for converting Asciidoc with custom macros, etc. I should use 1.6 and it should be stable enough right now to be used? I was a bit wary to do so initially as I wasn't sure how many changes were still to come.

mojavelinux commented 8 years ago

I should use 1.6 and it should be stable enough right now to be used?

Yes. In many ways, AsciidoctorJ 1.6.0 is more stable than 1.5.4 (in terms of runtime stability).

I was a bit wary to do so initially as I wasn't sure how many changes were still to come.

Now that we got the AST the way we want it, I'd say it's pretty stable. We'll probably defer any further big changes to 1.7.0. Though, I defer to Robert on that.

The one exception to writing macros for 1.5.x is if you are distributing them to the general public. In that case, you'd have to write against 1.5.x or else you'd be forcing everyone to upgrade. When it's for your own uses (or internal), then we are recommending 1.6.0.

My goal is to have 1.6.0 out by sometime this summer. We need to get over this hump.