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.
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