My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

wimdeblauwe
Hi,

I am trying to create a simple AsciidoctorJ extension to get started. I have this build.gradle file:

[source,]
----
group 'org.asciidoctor.extension'
version '1.0-SNAPSHOT'

apply plugin: 'groovy'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.12'
    compile 'org.asciidoctor:asciidoctorj:1.5.5'

    testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
}
----

I have created this ExtensionRegistry implementation

[source,]
----
class InverseTextExtensionRegistry implements ExtensionRegistry {
    @Override
    void register(Asciidoctor asciidoctor) {
        asciidoctor.javaExtensionRegistry().block( 'inversetext', InverseTextBlock)
    }
}
----

Using this BlockProcessor implementation:

[source,]
----
class InverseTextBlock extends BlockProcessor {
    InverseTextBlock(String macroName, Map<String, Object> config) {
        super(macroName, config)
    }

    @Override
    Object process(AbstractBlock parent, Reader reader, Map<String, Object> attributes) {
        def content = reader.readLines().collect({ it.toString().reverse() })
        return createBlock(parent, 'paragraph', content, attributes, [:])
    }
}
----

I also created a file `META-INF/services/org.asciidoctor.extension.spi.ExtensionRegistry` with `org.asciidoctor.extension.inversetext.InverseTextExtensionRegistry` as content to register my extension.

Finally, I created a Spock test:

[source,]
----
class InverseTextBlockTest extends Specification {
    def "inversetext block inverse the given text"() {
        def asciidocInput = """= Example document

[inversetext]
This text should get inversed

This is some other text
"""
        def asciidoctor = Asciidoctor.Factory.create()
        def result = asciidoctor.convert(asciidocInput, new Options())

        expect: result.contains("desrevni teg dlouhs txet sihT")
    }
}
----

When I run this, I get:

Jul 18, 2017 9:54:40 AM org.asciidoctor.internal.JRubyAsciidoctor render
SEVERE: org.jruby.RubyNoMethodError

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:307)
        at org.asciidoctor.internal.JRubyAsciidoctor.render(JRubyAsciidoctor.java:415)
        at org.asciidoctor.internal.JRubyAsciidoctor.convert(JRubyAsciidoctor.java:517)
        at org.asciidoctor.extension.inversetext.InverseTextBlockTest.inversetext block inverse the given text(InverseTextBlockTest.groovy:18)
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?(/Users/wdb/.gradle/caches/modules-2/files-2.1/org.asciidoctor/asciidoctorj/1.5.5/15d22a1d8e3880c0632f81da474d48b873d5579/asciidoctorj-1.5.5.jar!/gems/asciidoctor-1.5.5/lib/asciidoctor/extensions.rb:907)
        at RUBY.next_block(/Users/wdb/.gradle/caches/modules-2/files-2.1/org.asciidoctor/asciidoctorj/1.5.5/15d22a1d8e3880c0632f81da474d48b873d5579/asciidoctorj-1.5.5.jar!/gems/asciidoctor-1.5.5/lib/asciidoctor/parser.rb:691)
        at RUBY.next_section(/Users/wdb/.gradle/caches/modules-2/files-2.1/org.asciidoctor/asciidoctorj/1.5.5/15d22a1d8e3880c0632f81da474d48b873d5579/asciidoctorj-1.5.5.jar!/gems/asciidoctor-1.5.5/lib/asciidoctor/parser.rb:315)
        at RUBY.parse(/Users/wdb/.gradle/caches/modules-2/files-2.1/org.asciidoctor/asciidoctorj/1.5.5/15d22a1d8e3880c0632f81da474d48b873d5579/asciidoctorj-1.5.5.jar!/gems/asciidoctor-1.5.5/lib/asciidoctor/parser.rb:67)
        at RUBY.parse(/Users/wdb/.gradle/caches/modules-2/files-2.1/org.asciidoctor/asciidoctorj/1.5.5/15d22a1d8e3880c0632f81da474d48b873d5579/asciidoctorj-1.5.5.jar!/gems/asciidoctor-1.5.5/lib/asciidoctor/document.rb:472)
        at RUBY.load(/Users/wdb/.gradle/caches/modules-2/files-2.1/org.asciidoctor/asciidoctorj/1.5.5/15d22a1d8e3880c0632f81da474d48b873d5579/asciidoctorj-1.5.5.jar!/gems/asciidoctor-1.5.5/lib/asciidoctor.rb:1347)
        at RUBY.convert(/Users/wdb/.gradle/caches/modules-2/files-2.1/org.asciidoctor/asciidoctorj/1.5.5/15d22a1d8e3880c0632f81da474d48b873d5579/asciidoctorj-1.5.5.jar!/gems/asciidoctor-1.5.5/lib/asciidoctor.rb:1465)
        at RUBY.convert(<script>:72)
        at org.jruby.gen.InterfaceImpl150460342.convert(org/jruby/gen/InterfaceImpl150460342.gen:13)
        ... 1 more


Any idea what I am doing wrong? I based my code on the documentation at http://asciidoctor.org/docs/asciidoctorj/#extension-api and the source code of the asciidoctorj-screenshot extension.

regards,

Wim
Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

abelsromero
I think you need to set the contexts of the block.


class InverseTextBlock extends BlockProcessor {

    private static Map<String, Object> configs = new HashMap<String, Object>() {
        {
            put("contexts", Arrays.asList(":paragraph"))
        }
    }

    InverseTextBlock(String macroName, Map<String, Object> config) {
        super(macroName, configs)
    }

    @Override
    Object process(AbstractBlock parent, Reader reader, Map<String, Object> attributes) {
        def content = reader.readLines().collect({ it.toString().reverse() })
        return createBlock(parent, 'paragraph', content, attributes, [:])
    }

}


https://github.com/asciidoctor/asciidoctorj/pull/305
Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

wimdeblauwe
Thanks! I changed it to this to make it work:

class InverseTextBlock extends BlockProcessor {
    InverseTextBlock(String macroName, Map<String, Object> config) {
        super(macroName, [contexts: [':paragraph']])
    }

    @Override
    Object process(AbstractBlock parent, Reader reader, Map<String, Object> attributes) {
        def content = reader.readLines().collect({ it.toString().reverse() })
        return createBlock(parent, 'paragraph', content, attributes, [:])
    }
}

What does this 'contexts' option actually mean? What are possible values? Does it impact how the block can be used?

regards,

Wim
Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

wimdeblauwe
Note: It only works for this:

[inversetext]
This text should get inversed

But it should also work for this:

[inversetext]
----
This text should get inversed
This line as well
----

What do I need to change for that?
Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

abelsromero
exactly, thing is the context defines the type of block to detect.

a `:open` block is a block with no limits. If you want to use `----`, that's a `:paragraph` block. Note that you can add more than one type of block to the extension.

I am aware the documentation is not perfect, I usually go the JavaDocs of the AsciidoctorJ v 1.6.0 version or look through issues :/

The types of blocks are here: https://github.com/asciidoctor/asciidoctorj/pull/305/commits/0543ec5305408b37dd23d5eca787fac5e1a9eba1

And then in the manual, you have more information about them also: http://asciidoctor.org/docs/user-manual/#literal-text-and-blocks.


Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

mojavelinux
Administrator
> exactly, thing is the context defines the type of block to detect.

Technically, it matches the structural container (the precursor of a block type). This essentially means the delimited block lines. For example, "open" is two hyphens, "example" is four equals, etc.

The reason there is a difference is because an admonition block is actually a specialized example block. So you can see there are two levels of specificity.

-Dan

On Tue, Jul 18, 2017 at 8:12 AM, abelsromero [via Asciidoctor :: Discussion] <[hidden email]> wrote:
exactly, thing is the context defines the type of block to detect.

a `:open` block is a block with no limits. If you want to use `----`, that's a `:paragraph` block. Note that you can add more than one type of block to the extension.

I am aware the documentation is not perfect, I usually go the JavaDocs of the AsciidoctorJ v 1.6.0 version or look through issues :/

The types of blocks are here: https://github.com/asciidoctor/asciidoctorj/pull/305/commits/0543ec5305408b37dd23d5eca787fac5e1a9eba1

And then in the manual, you have more information about them also: http://asciidoctor.org/docs/user-manual/#literal-text-and-blocks.





If you reply to this email, your message will be added to the discussion below:
http://discuss.asciidoctor.org/My-first-AsciidoctorJ-extension-undefined-method-include-for-nil-NilClass-tp5736p5740.html
To start a new topic under Asciidoctor :: Discussion, email [hidden email]
To unsubscribe from Asciidoctor :: Discussion, click here.
NAML



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux
Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

mojavelinux
Administrator
In reply to this post by abelsromero
InverseTextBlock(String macroName, Map<String, Object> config) { 
    super(macroName, [contexts: [':paragraph']]) 
}


-Dan

On Sun, Aug 6, 2017 at 2:17 PM, Dan Allen <[hidden email]> wrote:
> exactly, thing is the context defines the type of block to detect.

Technically, it matches the structural container (the precursor of a block type). This essentially means the delimited block lines. For example, "open" is two hyphens, "example" is four equals, etc.

The reason there is a difference is because an admonition block is actually a specialized example block. So you can see there are two levels of specificity.

-Dan

On Tue, Jul 18, 2017 at 8:12 AM, abelsromero [via Asciidoctor :: Discussion] <[hidden email]> wrote:
exactly, thing is the context defines the type of block to detect.

a `:open` block is a block with no limits. If you want to use `----`, that's a `:paragraph` block. Note that you can add more than one type of block to the extension.

I am aware the documentation is not perfect, I usually go the JavaDocs of the AsciidoctorJ v 1.6.0 version or look through issues :/

The types of blocks are here: https://github.com/asciidoctor/asciidoctorj/pull/305/commits/0543ec5305408b37dd23d5eca787fac5e1a9eba1

And then in the manual, you have more information about them also: http://asciidoctor.org/docs/user-manual/#literal-text-and-blocks.





If you reply to this email, your message will be added to the discussion below:
http://discuss.asciidoctor.org/My-first-AsciidoctorJ-extension-undefined-method-include-for-nil-NilClass-tp5736p5740.html
To start a new topic under Asciidoctor :: Discussion, email [hidden email]
To unsubscribe from Asciidoctor :: Discussion, click here.
NAML



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux
Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

mojavelinux
Administrator
In reply to this post by abelsromero
If you want the block extension to apply to a paragraph that has your custom block name (aka style), then you set the context to ":paragraph". If you want it to match a listing block that has your custom block name, then you also need to set the context to ":listing".

As of Asciidoctor 1.5.6, you can access the context that was matched via the attribute "cloaked-context" in the attribute list that is passed to the process method. That may affect what node you want to return or how you want to process it.

-Dan


On Sun, Aug 6, 2017 at 2:21 PM, Dan Allen <[hidden email]> wrote:
InverseTextBlock(String macroName, Map<String, Object> config) { 
    super(macroName, [contexts: [':paragraph']]) 
}


-Dan

On Sun, Aug 6, 2017 at 2:17 PM, Dan Allen <[hidden email]> wrote:
> exactly, thing is the context defines the type of block to detect.

Technically, it matches the structural container (the precursor of a block type). This essentially means the delimited block lines. For example, "open" is two hyphens, "example" is four equals, etc.

The reason there is a difference is because an admonition block is actually a specialized example block. So you can see there are two levels of specificity.

-Dan

On Tue, Jul 18, 2017 at 8:12 AM, abelsromero [via Asciidoctor :: Discussion] <[hidden email]> wrote:
exactly, thing is the context defines the type of block to detect.

a `:open` block is a block with no limits. If you want to use `----`, that's a `:paragraph` block. Note that you can add more than one type of block to the extension.

I am aware the documentation is not perfect, I usually go the JavaDocs of the AsciidoctorJ v 1.6.0 version or look through issues :/

The types of blocks are here: https://github.com/asciidoctor/asciidoctorj/pull/305/commits/0543ec5305408b37dd23d5eca787fac5e1a9eba1

And then in the manual, you have more information about them also: http://asciidoctor.org/docs/user-manual/#literal-text-and-blocks.





If you reply to this email, your message will be added to the discussion below:
http://discuss.asciidoctor.org/My-first-AsciidoctorJ-extension-undefined-method-include-for-nil-NilClass-tp5736p5740.html
To start a new topic under Asciidoctor :: Discussion, email [hidden email]
To unsubscribe from Asciidoctor :: Discussion, click here.
NAML



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux
Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

mojavelinux
Administrator
In reply to this post by abelsromero
And, btw, you should always use the constants for the contexts, not strings. So you'd use Contexts.CONTEXT_PARAGRAPH instead of ":paragraph".

-Dan

On Sun, Aug 6, 2017 at 2:24 PM, Dan Allen <[hidden email]> wrote:
If you want the block extension to apply to a paragraph that has your custom block name (aka style), then you set the context to ":paragraph". If you want it to match a listing block that has your custom block name, then you also need to set the context to ":listing".

As of Asciidoctor 1.5.6, you can access the context that was matched via the attribute "cloaked-context" in the attribute list that is passed to the process method. That may affect what node you want to return or how you want to process it.

-Dan


On Sun, Aug 6, 2017 at 2:21 PM, Dan Allen <[hidden email]> wrote:
InverseTextBlock(String macroName, Map<String, Object> config) { 
    super(macroName, [contexts: [':paragraph']]) 
}


-Dan

On Sun, Aug 6, 2017 at 2:17 PM, Dan Allen <[hidden email]> wrote:
> exactly, thing is the context defines the type of block to detect.

Technically, it matches the structural container (the precursor of a block type). This essentially means the delimited block lines. For example, "open" is two hyphens, "example" is four equals, etc.

The reason there is a difference is because an admonition block is actually a specialized example block. So you can see there are two levels of specificity.

-Dan

On Tue, Jul 18, 2017 at 8:12 AM, abelsromero [via Asciidoctor :: Discussion] <[hidden email]> wrote:
exactly, thing is the context defines the type of block to detect.

a `:open` block is a block with no limits. If you want to use `----`, that's a `:paragraph` block. Note that you can add more than one type of block to the extension.

I am aware the documentation is not perfect, I usually go the JavaDocs of the AsciidoctorJ v 1.6.0 version or look through issues :/

The types of blocks are here: https://github.com/asciidoctor/asciidoctorj/pull/305/commits/0543ec5305408b37dd23d5eca787fac5e1a9eba1

And then in the manual, you have more information about them also: http://asciidoctor.org/docs/user-manual/#literal-text-and-blocks.





If you reply to this email, your message will be added to the discussion below:
http://discuss.asciidoctor.org/My-first-AsciidoctorJ-extension-undefined-method-include-for-nil-NilClass-tp5736p5740.html
To start a new topic under Asciidoctor :: Discussion, email [hidden email]
To unsubscribe from Asciidoctor :: Discussion, click here.
NAML



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux
Reply | Threaded
Open this post in threaded view
|

Re: My first AsciidoctorJ extension - undefined method `include?' for nil:NilClass

mojavelinux
Administrator
In reply to this post by abelsromero
The root problem in this thread is reflected in an open issue:


-Dan

On Sun, Aug 6, 2017 at 2:27 PM, Dan Allen <[hidden email]> wrote:
And, btw, you should always use the constants for the contexts, not strings. So you'd use Contexts.CONTEXT_PARAGRAPH instead of ":paragraph".

-Dan

On Sun, Aug 6, 2017 at 2:24 PM, Dan Allen <[hidden email]> wrote:
If you want the block extension to apply to a paragraph that has your custom block name (aka style), then you set the context to ":paragraph". If you want it to match a listing block that has your custom block name, then you also need to set the context to ":listing".

As of Asciidoctor 1.5.6, you can access the context that was matched via the attribute "cloaked-context" in the attribute list that is passed to the process method. That may affect what node you want to return or how you want to process it.

-Dan


On Sun, Aug 6, 2017 at 2:21 PM, Dan Allen <[hidden email]> wrote:
InverseTextBlock(String macroName, Map<String, Object> config) { 
    super(macroName, [contexts: [':paragraph']]) 
}


-Dan

On Sun, Aug 6, 2017 at 2:17 PM, Dan Allen <[hidden email]> wrote:
> exactly, thing is the context defines the type of block to detect.

Technically, it matches the structural container (the precursor of a block type). This essentially means the delimited block lines. For example, "open" is two hyphens, "example" is four equals, etc.

The reason there is a difference is because an admonition block is actually a specialized example block. So you can see there are two levels of specificity.

-Dan

On Tue, Jul 18, 2017 at 8:12 AM, abelsromero [via Asciidoctor :: Discussion] <[hidden email]> wrote:
exactly, thing is the context defines the type of block to detect.

a `:open` block is a block with no limits. If you want to use `----`, that's a `:paragraph` block. Note that you can add more than one type of block to the extension.

I am aware the documentation is not perfect, I usually go the JavaDocs of the AsciidoctorJ v 1.6.0 version or look through issues :/

The types of blocks are here: https://github.com/asciidoctor/asciidoctorj/pull/305/commits/0543ec5305408b37dd23d5eca787fac5e1a9eba1

And then in the manual, you have more information about them also: http://asciidoctor.org/docs/user-manual/#literal-text-and-blocks.





If you reply to this email, your message will be added to the discussion below:
http://discuss.asciidoctor.org/My-first-AsciidoctorJ-extension-undefined-method-include-for-nil-NilClass-tp5736p5740.html
To start a new topic under Asciidoctor :: Discussion, email [hidden email]
To unsubscribe from Asciidoctor :: Discussion, click here.
NAML



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux



--
Dan Allen | @mojavelinux | https://twitter.com/mojavelinux