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.
@@ -9,32 +9,46 @@ begin
9
9
  require 'slim'
10
10
  rescue LoadError; end
11
11
 
12
- module Shortcode
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
- class << self
15
- attr_writer :configuration
18
+ # This is provided for backwards compatibility
19
+ def self.singleton
20
+ @instance ||= new
16
21
  end
17
22
 
18
- def self.process(string, additional_attributes=nil)
19
- Shortcode::Processor.new.process string, additional_attributes
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 self.setup
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 self.register_presenter(*presenters)
41
+ def register_presenter(*presenters)
27
42
  presenters.each do |presenter|
28
- Shortcode::Presenter.register presenter
43
+ configuration.register_presenter(presenter)
29
44
  end
30
45
  end
31
46
 
32
47
  private
33
48
 
34
- def self.configuration
35
- @configuration ||= Configuration.new
36
- end
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 = :erb
34
- @template_path = "app/views/shortcode_templates"
35
- @templates = nil
36
- @helpers = []
37
- @block_tags = []
38
- @self_closing_tags = []
39
- @attribute_quote_type = '"'
40
- @use_attribute_quotes = true
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
@@ -1,4 +1,4 @@
1
- module Shortcode
1
+ class Shortcode
2
2
 
3
3
  # Raised when the template file can not be found
4
4
  class TemplateNotFound < StandardError; end
@@ -1,43 +1,73 @@
1
- class Shortcode::Parser < Parslet::Parser
1
+ class Shortcode::Parser
2
+ def initialize(configuration)
3
+ @configuration = configuration
4
+ setup_rules
5
+ end
2
6
 
3
- rule(:block_tag) { match_any_of Shortcode.configuration.block_tags }
4
- rule(:self_closing_tag) { match_any_of Shortcode.configuration.self_closing_tags }
7
+ def parse(string)
8
+ klass_instance.parse(string)
9
+ end
5
10
 
6
- rule(:quotes) { str(Shortcode.configuration.attribute_quote_type) }
11
+ def open(*args)
12
+ klass_instance.open(*args)
13
+ end
7
14
 
8
- rule(:space) { str(' ').repeat(1) }
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
- rule(:key) { match('[a-zA-Z0-9\-_]').repeat(1) }
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
- rule(:value_with_quotes) { quotes >> (quotes.absent? >> any).repeat.as(:value) >> quotes }
17
- rule(:value_without_quotes) { quotes.absent? >> ( (str(']') | whitespace).absent? >> any ).repeat.as(:value) }
18
- rule(:value) { Shortcode.configuration.use_attribute_quotes ? value_with_quotes : (value_without_quotes | value_with_quotes) }
22
+ def klass_instance
23
+ @klass_instance ||= klass.new
24
+ end
19
25
 
20
- rule(:option) { key.as(:key) >> str('=') >> value }
21
- rule(:options) { (str(' ') >> option).repeat(1) }
22
- rule(:options?) { options.repeat(0, 1) }
26
+ def setup_rules
27
+ define_match_any_of
23
28
 
24
- rule(:open) { str('[') >> block_tag.as(:open) >> options?.as(:options) >> str(']') >> newline? }
25
- rule(:close) { str('[/') >> block_tag.as(:close) >> str(']') >> newline? }
26
- rule(:open_close) { str('[') >> self_closing_tag.as(:open_close) >> options?.as(:options) >> str(']') >> newline? }
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
- rule(:text) { ((close | block | open_close).absent? >> any).repeat(1).as(:text) }
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
- rule(:body) { (block | text | open_close).repeat.as(:body) }
32
- root(:body)
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
- private
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
- def match_any_of(tags)
37
- return str('') if tags.length < 1
38
- tags.map{ |tag| str(tag) }.inject do |tag_chain, tag|
39
- tag_chain.send :|, tag
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
@@ -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
- class << self
4
- attr_writer :presenters
5
-
6
- def presenters
7
- @presenters ||= {}
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
- def initialize_custom_presenter(name)
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
@@ -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
- def parser
10
- @parser ||= Shortcode::Parser.new
11
- end
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
@@ -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
- @binding = Shortcode::TemplateBinding.new(@name, attributes, content, additional_attributes)
5
+ @configuration = configuration
6
+ @binding = Shortcode::TemplateBinding.new(@name, @configuration, attributes, content, additional_attributes)
6
7
  end
7
8
 
8
9
  def markup
9
- return markup_from_config unless Shortcode.configuration.templates.nil?
10
- template_files.each do |path|
11
- return File.read(path) if File.file? path
12
- end
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
- def render_template
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
- def markup_from_config
36
- if Shortcode.configuration.templates.has_key? @name.to_sym
37
- Shortcode.configuration.templates[@name.to_sym]
38
- else
39
- raise Shortcode::TemplateNotFound, "Shortcode.configuration.templates does not contain the key #{@name.to_sym}"
40
- end
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
- def template_files
44
- template_paths.map do |filename|
45
- File.join Shortcode.configuration.template_path, filename
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
- def template_paths
50
- [ "#{@name}.html.#{Shortcode.configuration.template_parser.to_s}", "#{@name}.#{Shortcode.configuration.template_parser.to_s}" ]
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 = Shortcode::Presenter.new name, set_attributes(attributes), content, additional_attributes
6
- @name = name
7
- @attributes = presenter.attributes
8
- @content = presenter.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
- def set_attributes(attributes)
19
- hash = {}
20
- attributes.each { |o| hash[o[:key].to_sym] = o[:value] }
21
- hash
22
- end
19
+ attr_reader :configuration
23
20
 
24
- def include_helper_modules
25
- return unless Shortcode.configuration.helpers.any?
26
- class << self
27
- Shortcode.configuration.helpers.each do |helper|
28
- include helper
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 < Parslet::Transform
1
+ class Shortcode::Transformer
2
+ def initialize(configuration)
3
+ @configuration = configuration
4
+ setup_rules
5
+ end
2
6
 
3
- rule(text: simple(:text)) { String(text) }
4
- rule(
5
- open: simple(:name),
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
- rule(body: sequence(:strings)) { strings.join }
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