locomotive_plugins 1.0.0.beta2 → 1.0.0.beta3

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.
data/README.md CHANGED
@@ -39,6 +39,9 @@ different ID, simply supply the ID in the `register_plugin` call:
39
39
 
40
40
  LocomotivePlugins::register_plugin(BasicAuth, 'auth')
41
41
 
42
+ See the sections below for usage examples. Also, see the
43
+ [documentation](http://rubydoc.info/github/colibri-software/locomotive_plugins/).
44
+
42
45
  ### Initialization
43
46
 
44
47
  To initialize a plugin object, do not override the `initialize` method because
@@ -76,6 +79,12 @@ the Locomotive UI.
76
79
 
77
80
  ### Liquid
78
81
 
82
+ Plugins have the ability to add liquid drops, tags, and filters to
83
+ LocomotiveCMS. These liquid objects will only be accessible to sites which have
84
+ enabled the plugin. All liquid objects have access to
85
+ `@context.registers[:plugin_object]` which supplies the plugin object. This
86
+ gives access to the config hash and other plugin methods.
87
+
79
88
  #### Drops
80
89
 
81
90
  A plugin can add a liquid drop which can be accessed from page templates in
@@ -199,13 +208,31 @@ Here's an example of an HTML config file:
199
208
  <p class="inline-hints">My Hint</p>
200
209
  </li>
201
210
  <li>
202
- <label name="content_type_slugs">Content Types</label>
203
- <select name="content_type_slugs" multiple="multiple">
211
+ <label name="content_type_slug">Content Types</label>
212
+ <select name="content_type_slug" multiple="multiple">
204
213
  {{#each content_types}}
205
214
  <option value="{{ this.slug }}"> {{ this.name }}</option>
206
215
  {{/each}}
207
216
  </select>
208
217
  </li>
218
+ <li>
219
+ <label name="do_awesome_thing">Do the awesome thing?</label>
220
+ <input type="checkbox" name="do_awesome_thing">
221
+ </li>
222
+
223
+ The values of the input fields in this form will be put into the plugin
224
+ object's config hash. Any input coming from a checkbox field will be saved as a
225
+ boolean value, and other input fields will be saved as strings. The keys for
226
+ the hash are taken from the `name` attribute of each input field. So, if in the
227
+ previous example, the "My Plugin Config" field is filled with "Config Value", a
228
+ content type with slug "my\_content\_type" is selected, and the checkbox is
229
+ checked, the config hash will be as follows:
230
+
231
+ {
232
+ "my_plugin_config" => "Config Value",
233
+ "content_type_slug" => "my_content_type",
234
+ "do_awesome_thing" => true
235
+ }
209
236
 
210
237
  ### Database Models
211
238
 
@@ -24,7 +24,7 @@ module Locomotive
24
24
  # normally
25
25
  def prefixed_liquid_tags(prefix)
26
26
  self.liquid_tags.inject({}) do |hash, (tag_name, tag_class)|
27
- hash["#{prefix}_#{tag_name}"] = tag_subclass(tag_class)
27
+ hash["#{prefix}_#{tag_name}"] = tag_subclass(prefix, tag_class)
28
28
  hash
29
29
  end
30
30
  end
@@ -32,10 +32,14 @@ module Locomotive
32
32
  protected
33
33
 
34
34
  # Creates a nested subclass to handle rendering this tag
35
- def tag_subclass(tag_class)
35
+ def tag_subclass(prefix, tag_class)
36
36
  tag_class.class_eval <<-CODE
37
37
  class TagSubclass < #{tag_class.to_s}
38
38
  include ::Locomotive::Plugin::TagSubclassMethods
39
+
40
+ def self.prefix
41
+ '#{prefix}'
42
+ end
39
43
  end
40
44
  CODE
41
45
  tag_class::TagSubclass
@@ -46,39 +50,32 @@ module Locomotive
46
50
  # Gets the module to include as a filter in liquid. It prefixes the
47
51
  # filter methods with the given string
48
52
  def prefixed_liquid_filter_module(prefix)
49
- # Build up a string to eval into the module so we only need to reopen
50
- # it once
51
- strings_to_eval = []
53
+ # Create the module to be returned
54
+ @prefixed_liquid_filter_module = Module.new do
55
+ include ::Locomotive::Plugin::Liquid::PrefixedFilterModule
56
+ end
52
57
 
58
+ # Add the prefixed methods to the module
53
59
  raw_filter_modules = [self.class.liquid_filters].flatten.compact
54
60
  raw_filter_modules.each do |mod|
55
61
  mod.public_instance_methods.each do |meth|
56
- strings_to_eval << <<-CODE
57
- def #{prefix}_#{meth}(input)
58
- self._passthrough_filter_call_for_#{prefix}('#{meth}', input)
62
+ @prefixed_liquid_filter_module.module_eval do
63
+ define_method(:"#{prefix}_#{meth}") do |input|
64
+ self._passthrough_filter_call(prefix, meth, input)
59
65
  end
60
- CODE
66
+ end
61
67
  end
62
68
  end
63
69
 
64
- strings_to_eval << <<-CODE
70
+ # Add a method which returns the modules to include for this prefix
71
+ @prefixed_liquid_filter_module.module_eval do
65
72
  protected
66
73
 
67
- def _passthrough_object_for_#{prefix}
68
- @_passthrough_object_for_#{prefix} ||= \
69
- self._build_passthrough_object([#{raw_filter_modules.join(',')}])
74
+ define_method(:"_modules_for_#{prefix}") do
75
+ raw_filter_modules
70
76
  end
71
-
72
- def _passthrough_filter_call_for_#{prefix}(meth, input)
73
- self._passthrough_object_for_#{prefix}.public_send(meth, input)
74
- end
75
- CODE
76
-
77
- # Eval the dynamic methods in
78
- @prefixed_liquid_filter_module = Module.new do
79
- include ::Locomotive::Plugin::Liquid::PrefixedFilterModule
80
77
  end
81
- @prefixed_liquid_filter_module.class_eval strings_to_eval.join("\n")
78
+
82
79
  @prefixed_liquid_filter_module
83
80
  end
84
81
 
@@ -10,12 +10,51 @@ module Locomotive
10
10
 
11
11
  protected
12
12
 
13
+ # This method is overridden by LocomotiveCMS to provide custom
14
+ # functionality when a prefixed method is called
15
+ def filter_method_called(prefix, meth)
16
+ end
17
+
13
18
  def _build_passthrough_object(modules_to_extend)
14
- Object.new.tap do |obj|
15
- modules_to_extend.each do |mod|
16
- obj.extend(mod)
17
- end
19
+ obj = ::Liquid::Strainer.new(@context)
20
+
21
+ modules_to_extend.each do |mod|
22
+ obj.extend(mod)
23
+ end
24
+
25
+ obj
26
+ end
27
+
28
+ def _passthrough_object(prefix)
29
+ @_passthrough_objects ||= {}
30
+ obj = @_passthrough_objects[prefix]
31
+
32
+ # Return it if we have it
33
+ return obj if obj
34
+
35
+ # Otherwise, build it
36
+ modules_for_prefix_meth = :"_modules_for_#{prefix}"
37
+ if self.respond_to?(modules_for_prefix_meth)
38
+ modules = self.__send__(modules_for_prefix_meth)
39
+ else
40
+ modules = []
41
+ end
42
+
43
+ @_passthrough_objects[prefix] = self._build_passthrough_object(modules)
44
+ end
45
+
46
+ def _passthrough_filter_call(prefix, meth, input)
47
+ p = Proc.new do
48
+ self._passthrough_object(prefix).__send__(meth, input)
49
+ end
50
+
51
+ # Call hook and grab return value if it yields
52
+ ret = nil
53
+ self.filter_method_called(prefix, meth) do
54
+ ret = p.call
18
55
  end
56
+
57
+ ret || p.call
19
58
  end
20
59
 
21
60
  end
@@ -4,18 +4,37 @@ module Locomotive
4
4
  module Liquid
5
5
  # @private
6
6
  module TagSubclassMethods
7
+
7
8
  # Check to see if this tag is enabled in the liquid context and render
8
9
  # accordingly
9
10
  def render(context)
10
- enabled = context.registers[:enabled_plugin_tags]
11
- if enabled && enabled.include?(self.class)
12
- super
13
- elsif self.respond_to?(:render_disabled)
14
- self.render_disabled(context)
15
- else
16
- ''
11
+ enabled_tags = context.registers[:enabled_plugin_tags]
12
+ enabled = enabled_tags && enabled_tags.include?(self.class)
13
+
14
+ p = Proc.new do
15
+ if enabled
16
+ super
17
+ elsif self.respond_to?(:render_disabled)
18
+ self.render_disabled(context)
19
+ else
20
+ ''
21
+ end
22
+ end
23
+
24
+ ret = nil
25
+ rendering_tag(self.class.prefix, enabled, context) do
26
+ ret = p.call
17
27
  end
28
+ ret || p.call
18
29
  end
30
+
31
+ protected
32
+
33
+ # This method is overridden by LocomotiveCMS to provide custom
34
+ # functionality when the tag is rendering
35
+ def rendering_tag(prefix, enabled, context)
36
+ end
37
+
19
38
  end
20
39
  end
21
40
  end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LocomotivePlugins
2
- VERSION = '1.0.0.beta2'
2
+ VERSION = '1.0.0.beta3'
3
3
  end
@@ -7,6 +7,8 @@ module Locomotive
7
7
 
8
8
  describe '#prefixed_liquid_filter_module' do
9
9
 
10
+ let(:strainer) { ::Liquid::Strainer.new(::Liquid::Context.new) }
11
+
10
12
  before(:each) do
11
13
  @plugin_with_filter = PluginWithFilter.new({})
12
14
  end
@@ -22,10 +24,9 @@ module Locomotive
22
24
  end
23
25
 
24
26
  it 'the prefixed methods should pass through to the original methods' do
25
- obj = Object.new
26
- obj.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix'))
27
- obj.prefix_add_http('google.com').should == 'http://google.com'
28
- obj.prefix_add_http('http://google.com').should == 'http://google.com'
27
+ strainer.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix'))
28
+ strainer.prefix_add_http('google.com').should == 'http://google.com'
29
+ strainer.prefix_add_http('http://google.com').should == 'http://google.com'
29
30
  end
30
31
 
31
32
  it 'includes multiple filter modules for one plugin' do
@@ -38,13 +39,54 @@ module Locomotive
38
39
  it 'works if multiple prefixed modules are mixed into the same object' do
39
40
  @plugin_with_many_filter_modules = PluginWithManyFilterModules.new({})
40
41
 
41
- obj = Object.new
42
- obj.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix1'))
43
- obj.extend(@plugin_with_many_filter_modules.prefixed_liquid_filter_module('prefix2'))
42
+ strainer.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix1'))
43
+ strainer.extend(@plugin_with_many_filter_modules.prefixed_liquid_filter_module('prefix2'))
44
+
45
+ strainer.prefix1_add_http('google.com').should == 'http://google.com'
46
+ strainer.prefix2_add_newline('google.com').should == "google.com\n"
47
+ strainer.prefix2_remove_http('http://google.com').should == 'google.com'
48
+ end
49
+
50
+ it 'should call the filter_method_called hook each time a filter is called' do
51
+ # Keep track of how many times filter_method_called is called
52
+ Locomotive::Plugin::Liquid::PrefixedFilterModule.module_eval do
53
+ attr_reader :count, :prefix, :method
54
+
55
+ def filter_method_called(prefix, meth)
56
+ @count ||= 0
57
+ @count += 1
58
+ @prefix = prefix
59
+ @method = meth
60
+ yield
61
+ end
62
+ end
63
+
64
+ # Call filter methods
65
+ strainer.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix'))
66
+ strainer.prefix_add_http('google.com').should == 'http://google.com'
67
+ strainer.prefix_add_http('http://google.com').should == 'http://google.com'
68
+
69
+ # Make sure filter_method_called was called as expected
70
+ strainer.count.should == 2
71
+ strainer.prefix.should == 'prefix'
72
+ strainer.method.should == :add_http
73
+ end
74
+
75
+ it 'should give the current liquid context object to the passthrough objects' do
76
+ Locomotive::Plugin::Liquid::PrefixedFilterModule.module_eval do
77
+ attr_reader :context
78
+ def passthrough_objects ; @_passthrough_objects ; end
79
+ end
80
+
81
+ # Extend the module and create the passthrough object
82
+ strainer.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix'))
83
+ strainer.prefix_add_http('google.com').should == 'http://google.com'
44
84
 
45
- obj.prefix1_add_http('google.com').should == 'http://google.com'
46
- obj.prefix2_add_newline('google.com').should == "google.com\n"
47
- obj.prefix2_remove_http('http://google.com').should == 'google.com'
85
+ # Find the context of the passthrough object
86
+ obj = strainer.passthrough_objects['prefix']
87
+ def obj.context ; @context ; end
88
+
89
+ obj.context.should == strainer.context
48
90
  end
49
91
 
50
92
  end
@@ -110,6 +152,37 @@ module Locomotive
110
152
  template.render(@context).should == expected_output
111
153
  end
112
154
 
155
+ it 'should call the rendering_tag hook each time a tag is rendered' do
156
+ TagSubclassMethods.module_eval do
157
+ def rendering_tag(prefix, enabled, context)
158
+ context.registers[:rendering_tag][self.class] = {
159
+ prefix: prefix,
160
+ enabled: enabled
161
+ }
162
+ yield
163
+ end
164
+ end
165
+
166
+ expected_output = <<-TEMPLATE
167
+ <p>Some Text</p>
168
+ Some Text
169
+ TEMPLATE
170
+
171
+ @context.registers[:rendering_tag] = {}
172
+
173
+ register_tags(@prefixed_tags)
174
+ @enabled_tags << @prefixed_tags['prefix_paragraph']
175
+ template = ::Liquid::Template.parse(@raw_template)
176
+ template.render(@context).should == expected_output
177
+
178
+ paragraph_class = ::Locomotive::PluginWithTags::Paragraph::TagSubclass
179
+ newline_class = ::Locomotive::PluginWithTags::Newline::TagSubclass
180
+
181
+ hash = @context.registers[:rendering_tag]
182
+ hash[paragraph_class].should == { prefix: 'prefix', enabled: true }
183
+ hash[newline_class].should == { prefix: 'prefix', enabled: false }
184
+ end
185
+
113
186
  protected
114
187
 
115
188
  def register_tags(tags)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: locomotive_plugins
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta2
4
+ version: 1.0.0.beta3
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-23 00:00:00.000000000 Z
12
+ date: 2012-12-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: locomotive_liquid