locomotive_plugins 1.0.0.beta4 → 1.0.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,47 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ module Liquid
5
+ # @api internal
6
+ #
7
+ # Adds the plugin object to the liquid context.
8
+ module ContextHelpers
9
+
10
+ # Adds the plugin object to the liquid context object to be used by
11
+ # tags, filters, and drops. This method looks in the +:site+ register
12
+ # for an object which responds to +#plugin_object_for_id+ in order to
13
+ # populate the +:plugin_object+ register. If such an object does not
14
+ # exist, the method simply yields without altering the context object.
15
+ # Otherwise, after yielding, the context object is reset to its
16
+ # previous state.
17
+ #
18
+ # @param plugin_id [String] the plugin id to use
19
+ # @param context [Liquid::Context] the liquid context object
20
+ def self.add_plugin_object_to_context(plugin_id, context)
21
+ site = self.fetch_site(context)
22
+ if site
23
+ old = context.registers[:plugin_object]
24
+ obj = site.plugin_object_for_id(plugin_id)
25
+ context.registers[:plugin_object] = obj
26
+ yield
27
+ context.registers[:plugin_object] = old
28
+ else
29
+ yield
30
+ end
31
+ end
32
+
33
+ protected
34
+
35
+ # Fetch the site from the context assuming it exists and responds to
36
+ # the +#plugin_object_for_id+ method.
37
+ #
38
+ # @param context [Liquid::Context] the liquid context object
39
+ # @return the site object or +nil+
40
+ def self.fetch_site(context)
41
+ site = context.registers[:site]
42
+ site if site.respond_to?(:plugin_object_for_id)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ module Liquid
5
+ # @api internal
6
+ #
7
+ # Extension to liquid drops added by plugins.
8
+ module DropExtension
9
+
10
+ # Allow setting the plugin_id, but only once.
11
+ def set_plugin_id(plugin_id)
12
+ @_plugin_id ||= plugin_id
13
+ end
14
+
15
+ # Add the plugin object to the context when invoked (see
16
+ # Liquid::Drop#invoke_drop)
17
+ def invoke_drop(method)
18
+ value = nil
19
+
20
+ ContextHelpers.add_plugin_object_to_context(_plugin_id, @context) do
21
+ value = super
22
+ end
23
+
24
+ value
25
+ end
26
+ alias :[] :invoke_drop
27
+
28
+ private
29
+
30
+ # Plugin ID (see set_plugin_id).
31
+ attr_reader :_plugin_id
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -2,19 +2,19 @@
2
2
  module Locomotive
3
3
  module Plugin
4
4
  module Liquid
5
- # @private
5
+ # @api internal
6
+ #
6
7
  # This module provides functionality for the module which aggregates all
7
8
  # the prefixed filter methods. See
8
- # <tt>Locomotive::Plugin::Liquid#prefixed_liquid_filter_module</tt>
9
+ # <tt>Locomotive::Plugin::Liquid#prefixed_liquid_filter_module</tt>.
9
10
  module PrefixedFilterModule
10
11
 
11
12
  protected
12
13
 
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
-
14
+ # Build the object to use for passing through the non-prefixed methods.
15
+ #
16
+ # @param modules_to_extend [Array] the module for the passthrough object
17
+ # to extend
18
18
  def _build_passthrough_object(modules_to_extend)
19
19
  obj = ::Liquid::Strainer.new(@context)
20
20
 
@@ -25,6 +25,10 @@ module Locomotive
25
25
  obj
26
26
  end
27
27
 
28
+ # Get the passthrough object for the given prefix.
29
+ #
30
+ # @param prefix [String] the prefix to use
31
+ # @return the passthrough object for +prefix+
28
32
  def _passthrough_object(prefix)
29
33
  @_passthrough_objects ||= {}
30
34
  obj = @_passthrough_objects[prefix]
@@ -43,18 +47,21 @@ module Locomotive
43
47
  @_passthrough_objects[prefix] = self._build_passthrough_object(modules)
44
48
  end
45
49
 
50
+ # Passthrough method call with the given prefix and input.
51
+ #
52
+ # @param prefix [String] the prefix to use
53
+ # @param meth [Symbol] the method to call
54
+ # @param input [String] the input to the method
55
+ # @return the result of calling the method on the passthrough object
46
56
  def _passthrough_filter_call(prefix, meth, input)
47
- p = Proc.new do
48
- self._passthrough_object(prefix).__send__(meth, input)
49
- end
57
+ # Setup context object and call the passthrough
58
+ output = nil
50
59
 
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
60
+ ContextHelpers.add_plugin_object_to_context(prefix, @context) do
61
+ output = self._passthrough_object(prefix).__send__(meth, input)
55
62
  end
56
63
 
57
- ret || p.call
64
+ output
58
65
  end
59
66
 
60
67
  end
@@ -2,17 +2,25 @@
2
2
  module Locomotive
3
3
  module Plugin
4
4
  module Liquid
5
- # @private
5
+ # @api internal
6
+ #
7
+ # The methods shared by all tag subclasses.
6
8
  module TagSubclassMethods
7
9
 
8
10
  # Check to see if this tag is enabled in the liquid context and render
9
- # accordingly
11
+ # accordingly.
12
+ #
13
+ # @param context [Liquid::Context] the liquid context object
14
+ # @return the rendered content of the superclass using +render+ or
15
+ # +render_disabled+ as appropriate
10
16
  def render(context)
11
17
  enabled_tags = context.registers[:enabled_plugin_tags]
12
18
  enabled = enabled_tags && enabled_tags.include?(self.class)
13
19
 
14
- p = Proc.new do
15
- if enabled
20
+ output = nil
21
+
22
+ ContextHelpers.add_plugin_object_to_context(self.prefix, context) do
23
+ output = if enabled
16
24
  super
17
25
  elsif self.respond_to?(:render_disabled)
18
26
  self.render_disabled(context)
@@ -21,18 +29,12 @@ module Locomotive
21
29
  end
22
30
  end
23
31
 
24
- ret = nil
25
- rendering_tag(self.class.prefix, enabled, context) do
26
- ret = p.call
27
- end
28
- ret || p.call
32
+ output
29
33
  end
30
34
 
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)
35
+ # The prefix for this tag.
36
+ def prefix
37
+ self.class.prefix
36
38
  end
37
39
 
38
40
  end
@@ -6,39 +6,3 @@ require 'haml'
6
6
  require 'mongoid'
7
7
 
8
8
  require 'locomotive/plugin'
9
-
10
- # The overall module for registering plugins
11
- module LocomotivePlugins
12
-
13
- # Get the default ID for the given plugin class
14
- #
15
- # @param plugin_class[Class] the class of the plugin object
16
- def self.default_id(plugin_class)
17
- plugin_class.to_s.split('::').last.underscore
18
- end
19
-
20
- # Register a plugin class with a given ID. If no ID is given, the default ID
21
- # is obtained by calling <tt>default_id(plugin_class)</tt>
22
- #
23
- # @param plugin_class[Class] the class pf the plugin to register
24
- # @param plugin_id[String] the plugin ID to use
25
- def self.register_plugin(plugin_class, plugin_id = nil)
26
- @@registered_plugins ||= {}
27
- plugin_id ||= self.default_id(plugin_class)
28
- @@registered_plugins[plugin_id] = plugin_class
29
- end
30
-
31
- # Get the hash of registered plugin classes, where the keys are the IDs which
32
- # were used to register the plugins
33
- #
34
- # @return [Hash<String, Class>] a hash of plugin IDs to plugin classes
35
- def self.registered_plugins
36
- @@registered_plugins ||= {}
37
- end
38
-
39
- # Remove all plugins from the registered list
40
- def self.clear_registered_plugins
41
- @@registered_plugins = {}
42
- end
43
-
44
- end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LocomotivePlugins
2
- VERSION = '1.0.0.beta4'
2
+ VERSION = '1.0.0.beta5'
3
3
  end
@@ -0,0 +1,48 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ module Locomotive
5
+ module Plugin
6
+ describe ClassTracker do
7
+
8
+ it 'tracks all the classes which include the module' do
9
+ classes = Locomotive::Plugin.plugin_classes
10
+ classes.should include(MyPlugin)
11
+ classes.should include(UselessPlugin)
12
+ end
13
+
14
+ it 'supports custom trackers' do
15
+ added_classes = []
16
+ num_added_classes = 0
17
+
18
+ Locomotive::Plugin.add_plugin_class_tracker do |plugin_class|
19
+ added_classes << plugin_class
20
+ end
21
+
22
+ Locomotive::Plugin.add_plugin_class_tracker do |plugin_class|
23
+ num_added_classes += 1
24
+ end
25
+
26
+ c = Class.new { include Locomotive::Plugin }
27
+ added_classes.should == [c]
28
+ num_added_classes.should == 1
29
+ end
30
+
31
+ it 'gives custom trackers access to the default ID' do
32
+ added_ids = []
33
+ Locomotive::Plugin.add_plugin_class_tracker do |plugin_class|
34
+ added_ids << plugin_class.default_plugin_id
35
+ end
36
+
37
+ MyNewPlugin = Class.new
38
+ MyNewPlugin.class_eval do
39
+ include Locomotive::Plugin
40
+ end
41
+
42
+ added_ids.count.should == 1
43
+ added_ids.first.should == 'my_new_plugin'
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,75 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ module Locomotive
5
+ module Plugin
6
+ module Liquid
7
+ describe ContextHelpers do
8
+
9
+ context '#add_plugin_object_to_context' do
10
+
11
+ before(:each) do
12
+ @config = {}
13
+ @plugin = MyPlugin.new(@config)
14
+ @context = ::Liquid::Context.new({}, {}, {site: @site}, true)
15
+
16
+ plugin = @plugin
17
+ @context.registers[:site] = stub do
18
+ stubs(:plugin_object_for_id).with('my_plugin').returns(plugin)
19
+ end
20
+ end
21
+
22
+ it 'should add the object to the context' do
23
+ did_yield = false
24
+ ContextHelpers.add_plugin_object_to_context('my_plugin', @context) do
25
+ did_yield = true
26
+ @context.registers[:plugin_object].should == @plugin
27
+ end
28
+ did_yield.should be_true
29
+ @context.registers[:plugin_object].should be_nil
30
+ end
31
+
32
+ it 'should reset the context object' do
33
+ initial_object = 'initial'
34
+ @context.registers[:plugin_object] = initial_object
35
+
36
+ did_yield = false
37
+ ContextHelpers.add_plugin_object_to_context('my_plugin', @context) do
38
+ did_yield = true
39
+ @context.registers[:plugin_object].should == @plugin
40
+ end
41
+ did_yield.should be_true
42
+ @context.registers[:plugin_object].should == initial_object
43
+ end
44
+
45
+ it 'should do nothing if there is no site object in the context' do
46
+ @context.registers[:site] = nil
47
+
48
+ did_yield = false
49
+ ContextHelpers.add_plugin_object_to_context('my_plugin', @context) do
50
+ did_yield = true
51
+ @context.registers[:plugin_object].should be_nil
52
+ end
53
+ did_yield.should be_true
54
+ @context.registers[:plugin_object].should be_nil
55
+ end
56
+
57
+ it 'should do nothing if the site object has no plugin_object_for method' do
58
+ @context.registers[:site] = Object.new
59
+
60
+ did_yield = false
61
+ ContextHelpers.add_plugin_object_to_context('my_plugin', @context) do
62
+ did_yield = true
63
+ @context.registers[:plugin_object].should be_nil
64
+ end
65
+ did_yield.should be_true
66
+ @context.registers[:plugin_object].should be_nil
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -5,73 +5,95 @@ module Locomotive
5
5
  module Plugin
6
6
  describe Liquid do
7
7
 
8
+ context '#setup_liquid_context' do
9
+
10
+ before(:each) do
11
+ @config = {}
12
+ @plugin = MyPlugin.new(@config)
13
+ @context = ::Liquid::Context.new({}, {}, {}, true)
14
+ @plugin.setup_liquid_context('my_plugin', @context)
15
+ end
16
+
17
+ it 'should add a container for the plugin liquid drops' do
18
+ @context['plugins.my_plugin'].class.should == MyPlugin::MyDrop
19
+ end
20
+
21
+ it 'should add a set of enabled liquid tags' do
22
+ @context.registers[:enabled_plugin_tags].class.should == Set
23
+ @context.registers[:enabled_plugin_tags].size.should == 1
24
+ @context.registers[:enabled_plugin_tags].should include(MyPlugin::MyTag::TagSubclass)
25
+ end
26
+
27
+ it 'should add liquid filters' do
28
+ @context.strainer.my_plugin_filter('input').should == 'input'
29
+ expect { @context.strainer.language_plugin_filter('input') }.to raise_error
30
+ end
31
+
32
+ it 'should add the plugin object to the context when invoking drops' do
33
+ ContextHelpers.expects(:add_plugin_object_to_context).with(
34
+ 'my_plugin', @context)
35
+ ::Liquid::Template.parse(
36
+ '{{ plugins.my_plugin.dummy_method }}').render(@context)
37
+ end
38
+
39
+ it 'should add the plugin object to the context when calling filters' do
40
+ ContextHelpers.expects(:add_plugin_object_to_context).with(
41
+ 'my_plugin', @context)
42
+ ::Liquid::Template.parse(
43
+ '{{ "test" | my_plugin_filter }}').render(@context)
44
+ end
45
+
46
+ it 'should add the plugin object to the context when rendering tags' do
47
+ MyPlugin.register_tags('my_plugin')
48
+ ContextHelpers.expects(:add_plugin_object_to_context).with(
49
+ 'my_plugin', @context)
50
+ ::Liquid::Template.parse('{% my_plugin_my_tag %}').render(@context)
51
+ end
52
+
53
+ end
54
+
8
55
  describe '#prefixed_liquid_filter_module' do
9
56
 
10
57
  let(:strainer) { ::Liquid::Strainer.new(::Liquid::Context.new) }
11
58
 
12
59
  before(:each) do
13
- @plugin_with_filter = PluginWithFilter.new({})
60
+ @plugin_with_filter_class = PluginWithFilter
14
61
  end
15
62
 
16
63
  it 'should contain all prefixed methods for provided filter modules' do
17
- mod = @plugin_with_filter.prefixed_liquid_filter_module('prefix')
64
+ mod = @plugin_with_filter_class.prefixed_liquid_filter_module('prefix')
18
65
  mod.public_instance_methods.should include(:prefix_add_http)
19
66
  end
20
67
 
21
68
  it 'should not contain any of the original methods' do
22
- mod = @plugin_with_filter.prefixed_liquid_filter_module('prefix')
69
+ mod = @plugin_with_filter_class.prefixed_liquid_filter_module('prefix')
23
70
  mod.public_instance_methods.should_not include(:add_http)
24
71
  end
25
72
 
26
73
  it 'the prefixed methods should pass through to the original methods' do
27
- strainer.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix'))
74
+ strainer.extend(@plugin_with_filter_class.prefixed_liquid_filter_module('prefix'))
28
75
  strainer.prefix_add_http('google.com').should == 'http://google.com'
29
76
  strainer.prefix_add_http('http://google.com').should == 'http://google.com'
30
77
  end
31
78
 
32
79
  it 'includes multiple filter modules for one plugin' do
33
- @plugin_with_many_filter_modules = PluginWithManyFilterModules.new({})
34
- mod = @plugin_with_many_filter_modules.prefixed_liquid_filter_module('prefix')
80
+ @plugin_with_many_filter_modules_class = PluginWithManyFilterModules
81
+ mod = @plugin_with_many_filter_modules_class.prefixed_liquid_filter_module('prefix')
35
82
  mod.public_instance_methods.should include(:prefix_add_newline)
36
83
  mod.public_instance_methods.should include(:prefix_remove_http)
37
84
  end
38
85
 
39
86
  it 'works if multiple prefixed modules are mixed into the same object' do
40
- @plugin_with_many_filter_modules = PluginWithManyFilterModules.new({})
87
+ @plugin_with_many_filter_modules_class = PluginWithManyFilterModules
41
88
 
42
- strainer.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix1'))
43
- strainer.extend(@plugin_with_many_filter_modules.prefixed_liquid_filter_module('prefix2'))
89
+ strainer.extend(@plugin_with_filter_class.prefixed_liquid_filter_module('prefix1'))
90
+ strainer.extend(@plugin_with_many_filter_modules_class.prefixed_liquid_filter_module('prefix2'))
44
91
 
45
92
  strainer.prefix1_add_http('google.com').should == 'http://google.com'
46
93
  strainer.prefix2_add_newline('google.com').should == "google.com\n"
47
94
  strainer.prefix2_remove_http('http://google.com').should == 'google.com'
48
95
  end
49
96
 
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
97
  it 'should give the current liquid context object to the passthrough objects' do
76
98
  Locomotive::Plugin::Liquid::PrefixedFilterModule.module_eval do
77
99
  attr_reader :context
@@ -79,7 +101,7 @@ module Locomotive
79
101
  end
80
102
 
81
103
  # Extend the module and create the passthrough object
82
- strainer.extend(@plugin_with_filter.prefixed_liquid_filter_module('prefix'))
104
+ strainer.extend(@plugin_with_filter_class.prefixed_liquid_filter_module('prefix'))
83
105
  strainer.prefix_add_http('google.com').should == 'http://google.com'
84
106
 
85
107
  # Find the context of the passthrough object
@@ -97,6 +119,11 @@ module Locomotive
97
119
  @plugin_class = PluginWithTags
98
120
  @prefixed_tags = @plugin_class.prefixed_liquid_tags('prefix')
99
121
 
122
+ # Clear out existing registered liquid tags and register the ones we
123
+ # want
124
+ ::Liquid::Template.instance_variable_set(:@tags, nil)
125
+ PluginWithTags.register_tags('prefix')
126
+
100
127
  @enabled_tags = []
101
128
  @context = ::Liquid::Context.new
102
129
  @context.registers[:enabled_plugin_tags] = @enabled_tags
@@ -113,13 +140,20 @@ module Locomotive
113
140
  @prefixed_tags['prefix_newline'].should be < PluginWithTags::Newline
114
141
  end
115
142
 
143
+ it 'should register all prefixed tags in liquid' do
144
+ ::Liquid::Template.tags.size.should == 2
145
+ ::Liquid::Template.tags['prefix_paragraph'].should be \
146
+ < PluginWithTags::Paragraph
147
+ ::Liquid::Template.tags['prefix_newline'].should be \
148
+ < PluginWithTags::Newline
149
+ end
150
+
116
151
  it 'only renders a tag if it is enabled in the liquid context' do
117
152
  expected_output = <<-TEMPLATE
118
153
  <p>Some Text</p>
119
154
  Some Text<br />
120
155
  TEMPLATE
121
156
 
122
- register_tags(@prefixed_tags)
123
157
  template = ::Liquid::Template.parse(@raw_template)
124
158
  template.render(@context).should_not == expected_output
125
159
 
@@ -134,7 +168,6 @@ module Locomotive
134
168
  Some Text
135
169
  TEMPLATE
136
170
 
137
- register_tags(@prefixed_tags)
138
171
  template = ::Liquid::Template.parse(@raw_template)
139
172
  template.render(@context).should == expected_output
140
173
  end
@@ -147,48 +180,8 @@ module Locomotive
147
180
  Some Text
148
181
  TEMPLATE
149
182
 
150
- register_tags(@prefixed_tags)
151
- template = ::Liquid::Template.parse(@raw_template)
152
- template.render(@context).should == expected_output
153
- end
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
183
  template = ::Liquid::Template.parse(@raw_template)
176
184
  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
-
186
- protected
187
-
188
- def register_tags(tags)
189
- tags.each do |name, klass|
190
- ::Liquid::Template.register_tag(name, klass)
191
- end
192
185
  end
193
186
 
194
187
  end