shortcode 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +7 -9
- data/Appraisals +8 -8
- data/CHANGELOG.md +14 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +41 -55
- data/README.md +66 -27
- data/gemfiles/rails_4.1.gemfile +3 -3
- data/gemfiles/rails_4.2.gemfile +3 -3
- data/gemfiles/rails_5.0.gemfile +8 -0
- data/gemfiles/rails_5.1.gemfile +8 -0
- data/lib/shortcode.rb +26 -12
- data/lib/shortcode/configuration.rb +20 -8
- data/lib/shortcode/exceptions.rb +1 -1
- data/lib/shortcode/parser.rb +59 -29
- data/lib/shortcode/presenter.rb +19 -26
- data/lib/shortcode/processor.rb +8 -9
- data/lib/shortcode/tag.rb +44 -31
- data/lib/shortcode/template_binding.rb +19 -18
- data/lib/shortcode/transformer.rb +34 -13
- data/lib/shortcode/version.rb +2 -2
- data/shortcode.gemspec +6 -6
- data/spec/parser_spec.rb +2 -2
- data/spec/presenter_spec.rb +4 -4
- data/spec/shortcode_spec.rb +25 -0
- data/spec/spec_helper.rb +4 -2
- data/spec/tag_spec.rb +23 -4
- data/spec/template_parsers_spec.rb +1 -1
- data/spec/transformer_spec.rb +3 -3
- metadata +17 -17
- data/gemfiles/rails_3.2.gemfile +0 -8
- data/gemfiles/rails_4.0.gemfile +0 -8
data/lib/shortcode.rb
CHANGED
@@ -9,32 +9,46 @@ begin
|
|
9
9
|
require 'slim'
|
10
10
|
rescue LoadError; end
|
11
11
|
|
12
|
-
|
12
|
+
class Shortcode
|
13
|
+
# This is providedc for backwards compatibility
|
14
|
+
def self.process(string, additional_attributes=nil)
|
15
|
+
singleton.process(string, additional_attributes)
|
16
|
+
end
|
13
17
|
|
14
|
-
|
15
|
-
|
18
|
+
# This is provided for backwards compatibility
|
19
|
+
def self.singleton
|
20
|
+
@instance ||= new
|
16
21
|
end
|
17
22
|
|
18
|
-
|
19
|
-
|
23
|
+
# This is providedc for backwards compatibility
|
24
|
+
def self.setup(&prc)
|
25
|
+
singleton.setup(&prc)
|
26
|
+
end
|
27
|
+
|
28
|
+
# This is providedc for backwards compatibility
|
29
|
+
def self.register_presenter(*presenters)
|
30
|
+
singleton.register_presenter(*presenters)
|
20
31
|
end
|
21
32
|
|
22
|
-
def
|
33
|
+
def process(string, additional_attributes=nil)
|
34
|
+
Shortcode::Processor.new.process(string, configuration, additional_attributes)
|
35
|
+
end
|
36
|
+
|
37
|
+
def setup
|
23
38
|
yield configuration
|
24
39
|
end
|
25
40
|
|
26
|
-
def
|
41
|
+
def register_presenter(*presenters)
|
27
42
|
presenters.each do |presenter|
|
28
|
-
|
43
|
+
configuration.register_presenter(presenter)
|
29
44
|
end
|
30
45
|
end
|
31
46
|
|
32
47
|
private
|
33
48
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
49
|
+
def configuration
|
50
|
+
@configuration ||= Configuration.new
|
51
|
+
end
|
38
52
|
end
|
39
53
|
|
40
54
|
require 'shortcode/version'
|
@@ -8,9 +8,15 @@ class Shortcode::Configuration
|
|
8
8
|
# Allows templates to be set from strings rather than read from the filesystem
|
9
9
|
attr_accessor :templates
|
10
10
|
|
11
|
+
# Allows setting whether templates on the configuration are checked first, or templates in the file system
|
12
|
+
attr_accessor :check_config_templates_first
|
13
|
+
|
11
14
|
# Assigns helper modules to be included in templates
|
12
15
|
attr_accessor :helpers
|
13
16
|
|
17
|
+
# Allows presenters to be set that can be used to process shortcode arguments before rendered
|
18
|
+
attr_accessor :presenters
|
19
|
+
|
14
20
|
# Set the supported block_tags
|
15
21
|
attr_reader :block_tags
|
16
22
|
def block_tags=(block_tags)
|
@@ -30,13 +36,19 @@ class Shortcode::Configuration
|
|
30
36
|
attr_accessor :use_attribute_quotes
|
31
37
|
|
32
38
|
def initialize
|
33
|
-
@template_parser
|
34
|
-
@template_path
|
35
|
-
@templates
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@
|
40
|
-
@
|
39
|
+
@template_parser = :erb
|
40
|
+
@template_path = "app/views/shortcode_templates"
|
41
|
+
@templates = {}
|
42
|
+
@check_config_templates_first = true
|
43
|
+
@helpers = []
|
44
|
+
@block_tags = []
|
45
|
+
@self_closing_tags = []
|
46
|
+
@attribute_quote_type = '"'
|
47
|
+
@use_attribute_quotes = true
|
48
|
+
@presenters = {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def register_presenter(presenter)
|
52
|
+
Shortcode::Presenter.register(self, presenter)
|
41
53
|
end
|
42
54
|
end
|
data/lib/shortcode/exceptions.rb
CHANGED
data/lib/shortcode/parser.rb
CHANGED
@@ -1,43 +1,73 @@
|
|
1
|
-
class Shortcode::Parser
|
1
|
+
class Shortcode::Parser
|
2
|
+
def initialize(configuration)
|
3
|
+
@configuration = configuration
|
4
|
+
setup_rules
|
5
|
+
end
|
2
6
|
|
3
|
-
|
4
|
-
|
7
|
+
def parse(string)
|
8
|
+
klass_instance.parse(string)
|
9
|
+
end
|
5
10
|
|
6
|
-
|
11
|
+
def open(*args)
|
12
|
+
klass_instance.open(*args)
|
13
|
+
end
|
7
14
|
|
8
|
-
|
9
|
-
rule(:space?) { space.maybe }
|
10
|
-
rule(:newline) { (str("\r\n") | str("\n")) >> space? }
|
11
|
-
rule(:newline?) { newline.maybe }
|
12
|
-
rule(:whitespace) { (space | newline).repeat(1) }
|
15
|
+
private
|
13
16
|
|
14
|
-
|
17
|
+
# This allows us to create a new class with the rules for the specific configuration
|
18
|
+
def klass
|
19
|
+
@klass ||= Class.new(Parslet::Parser)
|
20
|
+
end
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
22
|
+
def klass_instance
|
23
|
+
@klass_instance ||= klass.new
|
24
|
+
end
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
rule(:options?) { options.repeat(0, 1) }
|
26
|
+
def setup_rules
|
27
|
+
define_match_any_of
|
23
28
|
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
shortcode_configuration = @configuration
|
30
|
+
klass.rule(:block_tag) { match_any_of shortcode_configuration.block_tags }
|
31
|
+
klass.rule(:self_closing_tag) { match_any_of shortcode_configuration.self_closing_tags }
|
27
32
|
|
28
|
-
|
29
|
-
rule(:block) { (open >> (block | text | open_close).repeat.as(:inner) >> close) }
|
33
|
+
klass.rule(:quotes) { str(shortcode_configuration.attribute_quote_type) }
|
30
34
|
|
31
|
-
|
32
|
-
|
35
|
+
klass.rule(:space) { str(' ').repeat(1) }
|
36
|
+
klass.rule(:space?) { space.maybe }
|
37
|
+
klass.rule(:newline) { (str("\r\n") | str("\n")) >> space? }
|
38
|
+
klass.rule(:newline?) { newline.maybe }
|
39
|
+
klass.rule(:whitespace) { (space | newline).repeat(1) }
|
33
40
|
|
34
|
-
|
41
|
+
klass.rule(:key) { match('[a-zA-Z0-9\-_]').repeat(1) }
|
42
|
+
|
43
|
+
klass.rule(:value_with_quotes) { quotes >> (quotes.absent? >> any).repeat.as(:value) >> quotes }
|
44
|
+
klass.rule(:value_without_quotes) { quotes.absent? >> ( (str(']') | whitespace).absent? >> any ).repeat.as(:value) }
|
45
|
+
klass.rule(:value) { shortcode_configuration.use_attribute_quotes ? value_with_quotes : (value_without_quotes | value_with_quotes) }
|
46
|
+
|
47
|
+
klass.rule(:option) { key.as(:key) >> str('=') >> value }
|
48
|
+
klass.rule(:options) { (str(' ') >> option).repeat(1) }
|
49
|
+
klass.rule(:options?) { options.repeat(0, 1) }
|
35
50
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
51
|
+
klass.rule(:open) { str('[') >> block_tag.as(:open) >> options?.as(:options) >> str(']') >> newline? }
|
52
|
+
klass.rule(:close) { str('[/') >> block_tag.as(:close) >> str(']') >> newline? }
|
53
|
+
klass.rule(:open_close) { str('[') >> self_closing_tag.as(:open_close) >> options?.as(:options) >> str(']') >> newline? }
|
54
|
+
|
55
|
+
klass.rule(:text) { ((close | block | open_close).absent? >> any).repeat(1).as(:text) }
|
56
|
+
klass.rule(:block) { (open >> (block | text | open_close).repeat.as(:inner) >> close) }
|
57
|
+
|
58
|
+
klass.rule(:body) { (block | text | open_close).repeat.as(:body) }
|
59
|
+
klass.root(:body)
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_match_any_of
|
63
|
+
klass.send(:define_method, :match_any_of) do |tags|
|
64
|
+
if tags.length < 1
|
65
|
+
return str('')
|
66
|
+
else
|
67
|
+
tags.map{ |tag| str(tag) }.inject do |tag_chain, tag|
|
68
|
+
tag_chain.send :|, tag
|
69
|
+
end
|
40
70
|
end
|
41
71
|
end
|
42
|
-
|
72
|
+
end
|
43
73
|
end
|
data/lib/shortcode/presenter.rb
CHANGED
@@ -1,27 +1,19 @@
|
|
1
1
|
class Shortcode::Presenter
|
2
|
+
def self.register(configuration, presenter)
|
3
|
+
validate presenter
|
4
|
+
[*presenter.for].each { |k| configuration.presenters[k.to_sym] = presenter }
|
5
|
+
end
|
2
6
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
def register(presenter)
|
11
|
-
validate presenter
|
12
|
-
[*presenter.for].each { |k| presenters[k.to_sym] = presenter }
|
13
|
-
end
|
14
|
-
|
15
|
-
def validate(presenter)
|
16
|
-
raise ArgumentError, "The presenter must define the class method #for" unless presenter.respond_to?(:for)
|
17
|
-
raise ArgumentError, "The presenter must define an initialize method" unless presenter.private_instance_methods(false).include?(:initialize)
|
18
|
-
%w(content attributes).each do |method|
|
19
|
-
raise ArgumentError, "The presenter must define the method ##{method}" unless presenter.method_defined?(method.to_sym)
|
20
|
-
end
|
7
|
+
def self.validate(presenter)
|
8
|
+
raise ArgumentError, "The presenter must define the class method #for" unless presenter.respond_to?(:for)
|
9
|
+
raise ArgumentError, "The presenter must define an initialize method" unless presenter.private_instance_methods(false).include?(:initialize)
|
10
|
+
%w(content attributes).each do |method|
|
11
|
+
raise ArgumentError, "The presenter must define the method ##{method}" unless presenter.method_defined?(method.to_sym)
|
21
12
|
end
|
22
13
|
end
|
23
14
|
|
24
|
-
def initialize(name, attributes, content, additional_attributes)
|
15
|
+
def initialize(name, configuration, attributes, content, additional_attributes)
|
16
|
+
@configuration = configuration
|
25
17
|
@attributes = attributes
|
26
18
|
@content = content
|
27
19
|
@additional_attributes = additional_attributes
|
@@ -38,12 +30,13 @@ class Shortcode::Presenter
|
|
38
30
|
|
39
31
|
private
|
40
32
|
|
41
|
-
|
42
|
-
if Shortcode::Presenter.presenters.has_key? name.to_sym
|
43
|
-
presenter = Shortcode::Presenter.presenters[name.to_sym].new(@attributes, @content, @additional_attributes)
|
44
|
-
@attributes = presenter.attributes
|
45
|
-
@content = presenter.content
|
46
|
-
end
|
47
|
-
end
|
33
|
+
attr_reader :configuration
|
48
34
|
|
35
|
+
def initialize_custom_presenter(name)
|
36
|
+
if configuration.presenters.has_key? name.to_sym
|
37
|
+
presenter = configuration.presenters[name.to_sym].new(@attributes, @content, @additional_attributes)
|
38
|
+
@attributes = presenter.attributes
|
39
|
+
@content = presenter.content
|
40
|
+
end
|
41
|
+
end
|
49
42
|
end
|
data/lib/shortcode/processor.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
class Shortcode::Processor
|
2
2
|
|
3
|
-
def process(string, additional_attributes=nil)
|
4
|
-
transformer.apply parser.parse(string), additional_attributes: additional_attributes
|
3
|
+
def process(string, configuration, additional_attributes=nil)
|
4
|
+
transformer(configuration).apply parser(configuration).parse(string), additional_attributes: additional_attributes
|
5
5
|
end
|
6
6
|
|
7
7
|
private
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def transformer
|
14
|
-
@transformer ||= Shortcode::Transformer.new
|
15
|
-
end
|
9
|
+
def parser(configuration)
|
10
|
+
@parser ||= Shortcode::Parser.new(configuration)
|
11
|
+
end
|
16
12
|
|
13
|
+
def transformer(configuration)
|
14
|
+
@transformer ||= Shortcode::Transformer.new(configuration)
|
15
|
+
end
|
17
16
|
end
|
data/lib/shortcode/tag.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
class Shortcode::Tag
|
2
2
|
|
3
|
-
def initialize(name, attributes=[], content='', additional_attributes=nil)
|
3
|
+
def initialize(name, configuration, attributes=[], content='', additional_attributes=nil)
|
4
4
|
@name = name.downcase
|
5
|
-
@
|
5
|
+
@configuration = configuration
|
6
|
+
@binding = Shortcode::TemplateBinding.new(@name, @configuration, attributes, content, additional_attributes)
|
6
7
|
end
|
7
8
|
|
8
9
|
def markup
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
raise Shortcode::TemplateNotFound, "Searched in:", template_files
|
10
|
+
template = first_priority_template
|
11
|
+
template = second_priority_template if template.nil?
|
12
|
+
return template unless template.nil?
|
13
|
+
raise Shortcode::TemplateNotFound, "No template found for #{@name} in configuration or files"
|
14
14
|
end
|
15
15
|
|
16
16
|
def render
|
@@ -19,34 +19,47 @@ class Shortcode::Tag
|
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
|
-
|
23
|
-
case Shortcode.configuration.template_parser
|
24
|
-
when :erb
|
25
|
-
ERB.new(markup).result(@binding.get_binding)
|
26
|
-
when :haml
|
27
|
-
Haml::Engine.new(markup, ugly: true).render(@binding)
|
28
|
-
when :slim
|
29
|
-
Slim::Template.new { markup }.render(@binding)
|
30
|
-
else
|
31
|
-
raise Shortcode::TemplateParserNotSupported, Shortcode.configuration.template_parser
|
32
|
-
end
|
33
|
-
end
|
22
|
+
attr_reader :configuration
|
34
23
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
24
|
+
def render_template
|
25
|
+
case configuration.template_parser
|
26
|
+
when :erb
|
27
|
+
ERB.new(markup).result(@binding.get_binding)
|
28
|
+
when :haml
|
29
|
+
Haml::Engine.new(markup).render(@binding)
|
30
|
+
when :slim
|
31
|
+
Slim::Template.new { markup }.render(@binding)
|
32
|
+
else
|
33
|
+
raise Shortcode::TemplateParserNotSupported, configuration.template_parser
|
41
34
|
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def first_priority_template
|
38
|
+
configuration.check_config_templates_first ? markup_from_config : markup_from_file
|
39
|
+
end
|
40
|
+
|
41
|
+
def second_priority_template
|
42
|
+
configuration.check_config_templates_first ? markup_from_file : markup_from_config
|
43
|
+
end
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
45
|
+
def markup_from_file
|
46
|
+
template_files.each do |path|
|
47
|
+
return File.read(path) if File.file? path
|
47
48
|
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def markup_from_config
|
53
|
+
configuration.templates[@name.to_sym]
|
54
|
+
end
|
48
55
|
|
49
|
-
|
50
|
-
|
56
|
+
def template_files
|
57
|
+
template_paths.map do |filename|
|
58
|
+
File.join configuration.template_path, filename
|
51
59
|
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def template_paths
|
63
|
+
[ "#{@name}.html.#{configuration.template_parser.to_s}", "#{@name}.#{configuration.template_parser.to_s}" ]
|
64
|
+
end
|
52
65
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
class Shortcode::TemplateBinding
|
2
2
|
|
3
|
-
def initialize(name, attributes=[], content='', additional_attributes=nil)
|
3
|
+
def initialize(name, configuration, attributes=[], content='', additional_attributes=nil)
|
4
|
+
@configuration = configuration
|
4
5
|
include_helper_modules
|
5
|
-
presenter
|
6
|
-
@name
|
7
|
-
@attributes
|
8
|
-
@content
|
6
|
+
presenter = Shortcode::Presenter.new name, configuration, set_attributes(attributes), content, additional_attributes
|
7
|
+
@name = name
|
8
|
+
@attributes = presenter.attributes
|
9
|
+
@content = presenter.content
|
9
10
|
end
|
10
11
|
|
11
12
|
# Expose private binding() method for use with erb templates
|
@@ -15,19 +16,19 @@ class Shortcode::TemplateBinding
|
|
15
16
|
|
16
17
|
private
|
17
18
|
|
18
|
-
|
19
|
-
hash = {}
|
20
|
-
attributes.each { |o| hash[o[:key].to_sym] = o[:value] }
|
21
|
-
hash
|
22
|
-
end
|
19
|
+
attr_reader :configuration
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
21
|
+
def set_attributes(attributes)
|
22
|
+
hash = {}
|
23
|
+
attributes.each { |o| hash[o[:key].to_sym] = o[:value] }
|
24
|
+
hash
|
25
|
+
end
|
32
26
|
|
27
|
+
def include_helper_modules
|
28
|
+
return unless configuration.helpers.any?
|
29
|
+
|
30
|
+
configuration.helpers.each do |helper|
|
31
|
+
self.class.send(:include, helper)
|
32
|
+
end
|
33
|
+
end
|
33
34
|
end
|
@@ -1,17 +1,38 @@
|
|
1
|
-
class Shortcode::Transformer
|
1
|
+
class Shortcode::Transformer
|
2
|
+
def initialize(configuration)
|
3
|
+
@configuration = configuration
|
4
|
+
setup_rules
|
5
|
+
end
|
2
6
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
options: subtree(:options),
|
7
|
-
inner: sequence(:inner),
|
8
|
-
close: simple(:name)
|
9
|
-
) { Shortcode::Tag.new(name.to_s, options, inner.join, additional_attributes).render }
|
10
|
-
rule(
|
11
|
-
open_close: simple(:name),
|
12
|
-
options: subtree(:options)
|
13
|
-
) { Shortcode::Tag.new(name.to_s, options, '', additional_attributes).render }
|
7
|
+
def apply(str, options = {})
|
8
|
+
klass_instance.apply(str, options)
|
9
|
+
end
|
14
10
|
|
15
|
-
|
11
|
+
private
|
16
12
|
|
13
|
+
def klass
|
14
|
+
@klass ||= Class.new(Parslet::Transform)
|
15
|
+
end
|
16
|
+
|
17
|
+
def klass_instance
|
18
|
+
@klass_instance ||= klass.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup_rules
|
22
|
+
shortcode_configuration = @configuration
|
23
|
+
|
24
|
+
klass.rule(text: klass.send(:simple, :text)) { String(text) }
|
25
|
+
klass.rule(
|
26
|
+
open: klass.send(:simple, :name),
|
27
|
+
options: klass.send(:subtree, :options),
|
28
|
+
inner: klass.send(:sequence, :inner),
|
29
|
+
close: klass.send(:simple, :name)
|
30
|
+
) { Shortcode::Tag.new(name.to_s, shortcode_configuration, options, inner.join, additional_attributes).render }
|
31
|
+
klass.rule(
|
32
|
+
open_close: klass.send(:simple, :name),
|
33
|
+
options: klass.send(:subtree, :options)
|
34
|
+
) { Shortcode::Tag.new(name.to_s, shortcode_configuration, options, '', additional_attributes).render }
|
35
|
+
|
36
|
+
klass.rule(body: klass.send(:sequence, :strings)) { strings.join }
|
37
|
+
end
|
17
38
|
end
|