locomotive_plugins 1.0.0.beta4 → 1.0.0.beta5

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/CHANGELOG CHANGED
@@ -1,8 +1,10 @@
1
- ## 1.0.0
1
+ ## v1.0.0
2
2
 
3
3
  * Added liquid filters
4
4
  * Added liquid tags
5
5
  * Removed content type scoping (use liquid tags instead)
6
+ * Removed DBModels (just use Mongoid and LocomotiveCMS will isolate the models)
7
+ * Added class tracking (used by Locomotive)
6
8
 
7
9
  ## v0.2.1
8
10
 
data/README.md CHANGED
@@ -1,14 +1,15 @@
1
1
 
2
2
  # Locomotive Plugins [![Build Status](https://secure.travis-ci.org/colibri-software/locomotive_plugins.png)](https://secure.travis-ci.org/colibri-software/locomotive_plugins.png)
3
3
 
4
- Create plugins for [Locomotive CMS](http://locomotivecms.com/).
4
+ This gem is used to develop plugins for [Locomotive
5
+ CMS](http://locomotivecms.com/). Plugins can be enabled or disabled on each
6
+ site individually.
5
7
 
6
8
 
7
9
  ## Installation
8
10
 
9
11
  To create a Locomotive Plugin, create a ruby gem and then install this gem:
10
12
 
11
-
12
13
  gem install locomotive_plugins
13
14
 
14
15
  Alternatively if you're using Bundler, add the following line to your Gemfile:
@@ -19,27 +20,37 @@ and run `bundle install`.
19
20
 
20
21
  To install the plugin in LocomotiveCMS, simply [create a LocomotiveCMS
21
22
  app](http://doc.locomotivecms.com/installation/getting_started) and add your
22
- plugin gem to the app's Gemfile.
23
+ plugin gem to the app's Gemfile in the `locomotive_plugins` group:
24
+
25
+ group(:locomotive_plugins) do
26
+ gem 'my_plugin'
27
+ gem 'another_plugin'
28
+ end
23
29
 
24
30
 
25
31
  ## Usage
26
32
 
27
33
  To create a plugin, create a class which includes the `Locomotive::Plugin`
28
- module and register it as a plugin:
34
+ module:
29
35
 
30
36
  class BasicAuth
31
37
  include Locomotive::Plugin
32
38
  end
33
39
 
34
- LocomotivePlugins.register_plugin(BasicAuth)
40
+ The plugin class will automatically be registered under an ID which is its
41
+ underscored name, in this case, `basic_auth`. To register it under a different
42
+ ID, simply override the class level method `default_plugin_id`:
35
43
 
36
- The plugin will automatically be registered under an ID which is its
37
- underscored name, in this case, `basic_auth`. To register it under a
38
- different ID, simply supply the ID in the `register_plugin` call:
44
+ class BasicAuth
45
+ include Locomotive::Plugin
39
46
 
40
- LocomotivePlugins::register_plugin(BasicAuth, 'auth')
47
+ def self.default_plugin_id
48
+ 'auth'
49
+ end
50
+ end
41
51
 
42
- See the sections below for usage examples. Also, see the
52
+ See the sections below for usage examples of the various features. Also, see
53
+ the
43
54
  [documentation](http://rubydoc.info/github/colibri-software/locomotive_plugins/).
44
55
 
45
56
  ### Initialization
@@ -236,21 +247,35 @@ checked, the config hash will be as follows:
236
247
 
237
248
  ### Database Models
238
249
 
239
- Plugins can persist data in the database through the use of DBModels. A DBModel
240
- has all the functionality of a Mongoid document. For example:
250
+ Plugins can persist data in Locomotive's database through the use of Database
251
+ Models. A Database Model is simply a Mongoid document which is managed by
252
+ Locomotive CMS. For example:
241
253
 
242
- class VisitCount < Locomotive::Plugin::DBModel
254
+ class VisitCount
255
+ include Mongoid::Document
243
256
  field :count, default: 0
244
257
  end
245
258
 
246
259
  class VisitCounter
247
260
  include Locomotive::Plugin
248
261
 
249
- has_one :visit_count, VisitCount
250
262
  before_filter :increment_count
251
263
 
252
264
  def increment_count
253
- build_visit_count unless visit_count
254
265
  visit_count.count += 1
266
+ visit_count.save!
267
+ end
268
+
269
+ protected
270
+
271
+ def visit_count
272
+ @visit_count ||= (VisitCount.first || VisitCount.new)
255
273
  end
256
274
  end
275
+
276
+ Note that the plugin databases are isolated between Locomotive site instances.
277
+ In other words, if a plugin is enabled on two sites, A and B, and a request
278
+ comes in to site A which causes a Mongoid Document to be saved to the database,
279
+ this document will not be accessible to the plugin when a request comes in to
280
+ site B. Thus plugin database models should be developed in the context of a
281
+ single site, since each site will have its own database.
@@ -7,43 +7,47 @@ module Locomotive
7
7
 
8
8
  # Include this module in a class which should be registered as a Locomotive
9
9
  # plugin. See the documentation for the various methods which can be called
10
- # or overridden to describe the plugin
10
+ # or overridden to describe the plugin.
11
11
  module Plugin
12
12
 
13
+ include ClassTracker
13
14
  include ConfigUI
14
- include DBModels
15
15
  include Liquid
16
16
 
17
17
  # @private
18
+ #
19
+ # Set up the plugin class with some class methods, callbacks, and plugin
20
+ # class tracking.
21
+ #
22
+ # @param base the plugin class
18
23
  def self.included(base)
19
- self.add_db_model_class_methods(base)
20
24
  self.add_liquid_tag_methods(base)
25
+
26
+ base.class_eval do
27
+ extend ActiveModel::Callbacks
28
+ define_model_callbacks :filter
29
+ end
30
+
21
31
  base.extend ClassMethods
32
+
33
+ self.track_plugin_class(base)
22
34
  end
23
35
 
24
36
  module ClassMethods
25
- # Add a before filter to be called by the underlying controller
37
+ # Override this method to specify the default plugin_id to use when
38
+ # Locomotive registers the plugin. This plugin_id may be overridden by
39
+ # Locomotive CMS.
26
40
  #
27
- # @param meth[Symbol] the method to call
28
- #
29
- # @example
30
- # before_filter :my_method
31
- def before_filter(meth)
32
- @before_filters ||= []
33
- @before_filters << meth
34
- end
35
-
36
- # Get list of before filters
37
- #
38
- # @return [Array<Symbol>] an array of the method symbols
39
- def before_filters
40
- @before_filters ||= []
41
+ # @return by default, the underscored plugin class name (without the
42
+ # namespace)
43
+ def default_plugin_id
44
+ to_s.split('::').last.underscore
41
45
  end
42
46
 
43
47
  # Override this method to provide a module or array of modules to include
44
48
  # as liquid filters in the site. All public methods in the module will be
45
49
  # included as filters after being prefixed with the plugin id
46
- # (#\\{plugin_id}_#\\{method_name})
50
+ # (#\\{plugin_id}_#\\{method_name}).
47
51
  #
48
52
  # @example
49
53
  # class MyPlugin
@@ -59,7 +63,7 @@ module Locomotive
59
63
  # plugin. The return value must be a hash whose keys are the tag names
60
64
  # and values are the tag classes. The tag names will be included in
61
65
  # Locomotive's liquid renderer after being prefixed with the plugin id
62
- # (#\\{plugin_id}_#\\{tag_name})
66
+ # (#\\{plugin_id}_#\\{tag_name}).
63
67
  #
64
68
  # @example
65
69
  # class MyPlugin
@@ -70,76 +74,33 @@ module Locomotive
70
74
  def liquid_tags
71
75
  {}
72
76
  end
73
-
74
- # Create a +has_many+ mongoid relationship to objects of the given class.
75
- # The given class should be derived from Locomotive::Plugin::DBModel.
76
- # This will add the following methods to the plugin object:
77
- #
78
- # [#\\{name}] Returns a list of persisted objects
79
- #
80
- # [#\\{name}=] Setter for the list of persisted objects
81
- #
82
- # @param name[String] the name of the relationship
83
- #
84
- # @param klass[Class] the class of the objects to be stored
85
- def has_many(name, klass)
86
- self.create_has_many_relationship(name, klass)
87
- end
88
-
89
- # Create a +has_one+ mongoid relationship to an object of the given
90
- # class. The given class should be derived from
91
- # Locomotive::Plugin::DBModel. This will add the following methods to the
92
- # plugin object:
93
- #
94
- # [#\\{name}] Returns the related object
95
- #
96
- # [#\\{name}=] Setter for the related object
97
- #
98
- # [build_#\\{name}] Builds the related object
99
- #
100
- # [create_#\\{name}] Builds and saves the related object
101
- #
102
- # @param name[String] the name of the relationship
103
- #
104
- # @param klass[Class] the class of the object to be stored
105
- def has_one(name, klass)
106
- self.create_has_one_relationship(name, klass)
107
- end
108
77
  end
109
78
 
110
79
  # This variable is set by LocomotiveCMS. It contains the controller which
111
- # is handling the current request
80
+ # is handling the current request.
112
81
  attr_accessor :controller
113
82
 
114
83
  # This variable is set by LocomotiveCMS. It contains the current
115
- # configuration hash for the plugin
84
+ # configuration hash for the plugin.
116
85
  attr_accessor :config
117
86
 
118
87
  # Initialize by supplying the current config parameters. Note that this
119
88
  # method should not be overridden for custom initialization of plugin
120
- # objects. Instead, override the initialize_plugin method. If given a
121
- # block, the block will be called with `self` as an argument before the
122
- # `initialize_plugin` method is called. This is used by LocomotiveCMS to
123
- # perform some custom initialization before the plugin's initialization is
124
- # called.
89
+ # objects. Instead, override the initialize_plugin method.
90
+ #
91
+ # @param config the configuration hash
125
92
  def initialize(config)
126
93
  self.config = config
127
- yield self if block_given?
128
94
  self.initialize_plugin
129
95
  end
130
96
 
131
97
  # Override this method to supply custom initialization code for the plugin
132
- # object. <b>Do not override the normal +initialize+ method</b>
98
+ # object. <b>Do not override the normal +initialize+ method</b>.
133
99
  def initialize_plugin
134
100
  end
135
101
 
136
- # Get all before filters which have been added to the controller
137
- def before_filters
138
- self.class.before_filters
139
- end
140
-
141
102
  # Override this method to provide a liquid drop which should be available
142
- # in the CMS
103
+ # in the CMS.
143
104
  def to_liquid
144
105
  nil
145
106
  end
@@ -155,9 +116,12 @@ module Locomotive
155
116
  # for the config UI. The HTML string may be a Handlebars.js template. By
156
117
  # default, this method will use the file supplied by the
157
118
  # +config_template_file+ method to construct the string (see
158
- # #config_template_file)
119
+ # #config_template_file).
120
+ #
121
+ # @return by default, the contents of +config_template_file+, parsed by
122
+ # HAML if needed
159
123
  def config_template_string
160
- self.default_config_template_string
124
+ self.default_config_template_string(self.config_template_file)
161
125
  end
162
126
 
163
127
  end
@@ -0,0 +1,54 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ # Tracks classes which include the Locomotive::Plugin module. Also allows
5
+ # external classes to provide a tracker. A tracker is a block which will be
6
+ # called when a class includes Locomotive::Plugin. The class object will be
7
+ # handed into the block as a parameter.
8
+ module ClassTracker
9
+
10
+ # Set up class tracking on the module which has included this module.
11
+ # This will set up the following methods:
12
+ #
13
+ # [plugin_classes] Returns the set of plugin classes which have
14
+ # included Locomotive::Plugin
15
+ #
16
+ # [track_plugin_class] Keeps track of the plugin class which has been
17
+ # added. Also calls all plugin trackers
18
+ #
19
+ # [add_plugin_class_tracker] Add a block which is called when a plugin
20
+ # class is tracked. The block will be given
21
+ # the class to be tracked
22
+ #
23
+ # @param mod the module to add the tracking to
24
+ def self.included(mod)
25
+ mod.instance_eval do
26
+ @plugin_classes = Set.new
27
+ @trackers = []
28
+
29
+ class << self
30
+ attr_reader :plugin_classes
31
+ end
32
+
33
+ def track_plugin_class(klass)
34
+ @plugin_classes << klass
35
+ _call_trackers(klass)
36
+ end
37
+
38
+ def add_plugin_class_tracker(&block)
39
+ @trackers << block
40
+ end
41
+
42
+ private
43
+
44
+ def _call_trackers(klass)
45
+ @trackers.each do |tracker|
46
+ tracker.call(klass)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -1,14 +1,21 @@
1
1
 
2
2
  module Locomotive
3
3
  module Plugin
4
- # @private
4
+ # @api internal
5
+ #
6
+ # Helpers for setting up configuration UI.
5
7
  module ConfigUI
6
8
 
7
9
  protected
8
10
 
9
- def default_config_template_string
10
- filepath = self.config_template_file
11
-
11
+ # By default, fetch the contents of the file given by
12
+ # +config_template_file+. If it is HAML, convert it to HTML. Then return
13
+ # the contents.
14
+ #
15
+ # @param filepath [String] the file path to read from
16
+ # @return the contents of the file at +filepath+, parsed by HAML if
17
+ # needed
18
+ def default_config_template_string(filepath)
12
19
  if filepath
13
20
  filepath = filepath.to_s
14
21
  if filepath.end_with?('haml')
@@ -1,27 +1,78 @@
1
1
 
2
2
  module Locomotive
3
3
  module Plugin
4
+ # This module adds liquid handling methods to the plugin class.
4
5
  module Liquid
5
6
 
6
7
  # @private
8
+ #
9
+ # Add class methods.
10
+ #
11
+ # @param base the class which includes this module
7
12
  def self.included(base)
8
13
  base.extend(ClassMethods)
9
14
  end
10
15
 
11
- # @private
16
+ # @api internal
12
17
  module ClassMethods
18
+ # Adds methods from LiquidClassMethods module.
19
+ #
20
+ # @param base the plugin class to extend LiquidClassMethods
13
21
  def add_liquid_tag_methods(base)
14
- base.extend(LiquidTagMethods)
22
+ base.extend(LiquidClassMethods)
15
23
  end
16
24
  end
17
25
 
18
- module LiquidTagMethods
26
+ # This module adds class methods to the plugin class in order to generate
27
+ # the prefixed liquid filter module and generate and register the
28
+ # prefixed liquid tag classes.
29
+ module LiquidClassMethods
30
+
31
+ # Gets the module to include as a filter in liquid. It prefixes the
32
+ # filter methods with the given string followed by an underscore.
33
+ #
34
+ # @param prefix [String] the prefix to use
35
+ # @return the module to use as a filter module
36
+ def prefixed_liquid_filter_module(prefix)
37
+ # Create the module to be returned
38
+ @prefixed_liquid_filter_module = Module.new do
39
+ include ::Locomotive::Plugin::Liquid::PrefixedFilterModule
40
+ end
41
+
42
+ # Add the prefixed methods to the module
43
+ raw_filter_modules = [self.liquid_filters].flatten.compact
44
+ raw_filter_modules.each do |mod|
45
+ mod.public_instance_methods.each do |meth|
46
+ @prefixed_liquid_filter_module.module_eval do
47
+ define_method(:"#{prefix}_#{meth}") do |input|
48
+ self._passthrough_filter_call(prefix, meth, input)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ # Add a method which returns the modules to include for this prefix
55
+ @prefixed_liquid_filter_module.module_eval do
56
+ protected
57
+
58
+ define_method(:"_modules_for_#{prefix}") do
59
+ raw_filter_modules
60
+ end
61
+ end
62
+
63
+ @prefixed_liquid_filter_module
64
+ end
19
65
 
20
66
  # Returns a hash of tag names and tag classes to be registered in the
21
- # liquid environment. The tag names are prefixed by the given prefix,
22
- # and the tag classes are modified so that they check the liquid
23
- # context to determine whether they are enabled and should render
24
- # normally
67
+ # liquid environment. The tag names are prefixed by the given prefix
68
+ # followed by an underscore, and the tag classes are modified so that
69
+ # they check the liquid context to determine whether they are enabled
70
+ # and should render normally. This check is done by determining whether
71
+ # the tag class is in the +:enabled_plugin_tags+ register in the liquid
72
+ # context object (see +setup_liquid_context+).
73
+ #
74
+ # @param prefix [String] the prefix to use
75
+ # @return a hash of tag names to tag classes
25
76
  def prefixed_liquid_tags(prefix)
26
77
  self.liquid_tags.inject({}) do |hash, (tag_name, tag_class)|
27
78
  hash["#{prefix}_#{tag_name}"] = tag_subclass(prefix, tag_class)
@@ -29,9 +80,23 @@ module Locomotive
29
80
  end
30
81
  end
31
82
 
83
+ # Registers the tags given by +prefixed_liquid_tags+ in the liquid
84
+ # template system.
85
+ #
86
+ # @param prefix [String] the prefix to give to +prefixed_liquid_tags+
87
+ def register_tags(prefix)
88
+ prefixed_liquid_tags(prefix).each do |tag_name, tag_class|
89
+ ::Liquid::Template.register_tag(tag_name, tag_class)
90
+ end
91
+ end
92
+
32
93
  protected
33
94
 
34
- # Creates a nested subclass to handle rendering this tag
95
+ # Creates a nested subclass to handle rendering the given tag.
96
+ #
97
+ # @param prefix [String] the prefix for the tag class
98
+ # @param tag_class the liquid tag class to subclass
99
+ # @return the appropriate tag subclass
35
100
  def tag_subclass(prefix, tag_class)
36
101
  tag_class.class_eval <<-CODE
37
102
  class TagSubclass < #{tag_class.to_s}
@@ -47,36 +112,30 @@ module Locomotive
47
112
 
48
113
  end
49
114
 
50
- # Gets the module to include as a filter in liquid. It prefixes the
51
- # filter methods with the given string
52
- def prefixed_liquid_filter_module(prefix)
53
- # Create the module to be returned
54
- @prefixed_liquid_filter_module = Module.new do
55
- include ::Locomotive::Plugin::Liquid::PrefixedFilterModule
56
- end
57
-
58
- # Add the prefixed methods to the module
59
- raw_filter_modules = [self.class.liquid_filters].flatten.compact
60
- raw_filter_modules.each do |mod|
61
- mod.public_instance_methods.each do |meth|
62
- @prefixed_liquid_filter_module.module_eval do
63
- define_method(:"#{prefix}_#{meth}") do |input|
64
- self._passthrough_filter_call(prefix, meth, input)
65
- end
66
- end
67
- end
115
+ # Setup the liquid context object for rendering plugin liquid code. It
116
+ # will add to the context:
117
+ #
118
+ # * the prefixed tags. These will go into the +:enabled_plugin_tags+
119
+ # register.
120
+ # * the drop to the hash in the +"plugins"+ liquid variable
121
+ # * the prefixed filters.
122
+ #
123
+ # @param plugin_id [String] the plugin id to use
124
+ # @param context [Liquid::Context] the liquid context object
125
+ def setup_liquid_context(plugin_id, context)
126
+ # Add tags
127
+ (context.registers[:enabled_plugin_tags] ||= Set.new).tap do |set|
128
+ set.merge(self.class.prefixed_liquid_tags(plugin_id).values)
68
129
  end
69
130
 
70
- # Add a method which returns the modules to include for this prefix
71
- @prefixed_liquid_filter_module.module_eval do
72
- protected
73
-
74
- define_method(:"_modules_for_#{prefix}") do
75
- raw_filter_modules
76
- end
77
- end
131
+ # Add drop with extension
132
+ drop = self.to_liquid
133
+ drop.extend(Locomotive::Plugin::Liquid::DropExtension)
134
+ drop.set_plugin_id(plugin_id)
135
+ (context['plugins'] ||= {})[plugin_id] = drop
78
136
 
79
- @prefixed_liquid_filter_module
137
+ # Add filters
138
+ context.add_filters(self.class.prefixed_liquid_filter_module(plugin_id))
80
139
  end
81
140
 
82
141
  end