locomotive_plugins 1.0.0.beta2

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.
@@ -0,0 +1,10 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ # All classes to be persisted by a plugin should inherit from this class
5
+ # (see Locomotive::Plugin::ClassMethods #has_many or #has_one)
6
+ class DBModel
7
+ include ::Mongoid::Document
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ # The superclass of the DBModelContainer object for each plugin. This
5
+ # container has the actual relationships to the database objects
6
+ class DBModelContainer
7
+ include Mongoid::Document
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,86 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+
5
+ # Utility methods for dealing with DB Models
6
+ module DBModels
7
+
8
+ # @private
9
+ def self.included(base)
10
+ base.extend ClassMethods
11
+ end
12
+
13
+ # @private
14
+ module ClassMethods
15
+
16
+ def add_db_model_class_methods(base)
17
+ base.class_eval <<-CODE
18
+ class DBModelContainer < ::Locomotive::Plugin::DBModelContainer
19
+ end
20
+
21
+ def self.db_model_container_class
22
+ DBModelContainer
23
+ end
24
+ CODE
25
+ base.extend DBModelClassMethods
26
+ end
27
+
28
+ end
29
+
30
+ # @private
31
+ module DBModelClassMethods
32
+
33
+ protected
34
+
35
+ def create_has_many_relationship(name, klass)
36
+ self.db_model_container_class.embeds_many(name,
37
+ class_name: klass.to_s, inverse_of: :db_model_container)
38
+ klass.embedded_in(:db_model_container,
39
+ class_name: db_model_container_class.to_s, inverse_of: name)
40
+
41
+ self.define_passthrough_methods_to_container(name, "#{name}=")
42
+ end
43
+
44
+ def create_has_one_relationship(name, klass)
45
+ self.db_model_container_class.embeds_one(name,
46
+ class_name: klass.to_s, inverse_of: :db_model_container)
47
+ klass.embedded_in(:db_model_container,
48
+ class_name: db_model_container_class.to_s, inverse_of: name)
49
+
50
+ self.define_passthrough_methods_to_container(name, "#{name}=",
51
+ "build_#{name}", "create_#{name}")
52
+ end
53
+
54
+ def define_passthrough_methods_to_container(*methods)
55
+ class_eval <<-EOF
56
+ %w{#{methods.join(' ')}}.each do |meth|
57
+ define_method(meth) do |*args|
58
+ @db_model_container.send(meth, *args)
59
+ end
60
+ end
61
+ EOF
62
+ end
63
+
64
+ end
65
+
66
+ # Save the DB Model container
67
+ def save_db_model_container
68
+ self.db_model_container.save
69
+ end
70
+
71
+ # Get the DB Model container
72
+ def db_model_container
73
+ @db_model_container || load_or_create_db_model_container!
74
+ end
75
+
76
+ protected
77
+
78
+ def load_or_create_db_model_container!
79
+ @db_model_container = self.class.db_model_container_class.first \
80
+ || self.class.db_model_container_class.new
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,87 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ module Liquid
5
+
6
+ # @private
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ # @private
12
+ module ClassMethods
13
+ def add_liquid_tag_methods(base)
14
+ base.extend(LiquidTagMethods)
15
+ end
16
+ end
17
+
18
+ module LiquidTagMethods
19
+
20
+ # 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
25
+ def prefixed_liquid_tags(prefix)
26
+ self.liquid_tags.inject({}) do |hash, (tag_name, tag_class)|
27
+ hash["#{prefix}_#{tag_name}"] = tag_subclass(tag_class)
28
+ hash
29
+ end
30
+ end
31
+
32
+ protected
33
+
34
+ # Creates a nested subclass to handle rendering this tag
35
+ def tag_subclass(tag_class)
36
+ tag_class.class_eval <<-CODE
37
+ class TagSubclass < #{tag_class.to_s}
38
+ include ::Locomotive::Plugin::TagSubclassMethods
39
+ end
40
+ CODE
41
+ tag_class::TagSubclass
42
+ end
43
+
44
+ end
45
+
46
+ # Gets the module to include as a filter in liquid. It prefixes the
47
+ # filter methods with the given string
48
+ 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 = []
52
+
53
+ raw_filter_modules = [self.class.liquid_filters].flatten.compact
54
+ raw_filter_modules.each do |mod|
55
+ 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)
59
+ end
60
+ CODE
61
+ end
62
+ end
63
+
64
+ strings_to_eval << <<-CODE
65
+ protected
66
+
67
+ def _passthrough_object_for_#{prefix}
68
+ @_passthrough_object_for_#{prefix} ||= \
69
+ self._build_passthrough_object([#{raw_filter_modules.join(',')}])
70
+ 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
+ end
81
+ @prefixed_liquid_filter_module.class_eval strings_to_eval.join("\n")
82
+ @prefixed_liquid_filter_module
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ module Liquid
5
+ # @private
6
+ # This module provides functionality for the module which aggregates all
7
+ # the prefixed filter methods. See
8
+ # <tt>Locomotive::Plugin::Liquid#prefixed_liquid_filter_module</tt>
9
+ module PrefixedFilterModule
10
+
11
+ protected
12
+
13
+ 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
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ module Liquid
5
+ # @private
6
+ module TagSubclassMethods
7
+ # Check to see if this tag is enabled in the liquid context and render
8
+ # accordingly
9
+ 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
+ ''
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'liquid'
5
+ require 'haml'
6
+ require 'mongoid'
7
+
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 ADDED
@@ -0,0 +1,3 @@
1
+ module LocomotivePlugins
2
+ VERSION = '1.0.0.beta2'
3
+ end
@@ -0,0 +1,2 @@
1
+ %li
2
+ %p This is my template!
@@ -0,0 +1,3 @@
1
+ <li>
2
+ <p>This is my template!</p>
3
+ </li>
@@ -0,0 +1,41 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ module Locomotive
5
+ module Plugin
6
+ describe ConfigUI do
7
+
8
+ before(:each) do
9
+ @config = {}
10
+ @plugin = MyPlugin.new(@config)
11
+ @plugin_with_non_string_path = PluginWithNonStringPath.new(@config)
12
+ @another_plugin = MyOtherPlugin.new(@config)
13
+ @useless_plugin = UselessPlugin.new(@config)
14
+ end
15
+
16
+ it 'should return the template string of an HTML file' do
17
+ @plugin = MyPlugin.new({})
18
+ filepath = @plugin.config_template_file
19
+ @plugin.config_template_string.should == IO.read(filepath)
20
+ end
21
+
22
+ it 'should handle non-string paths' do
23
+ filepath = @plugin_with_non_string_path.config_template_file
24
+ template = @plugin_with_non_string_path.config_template_string
25
+ template.should == IO.read(filepath.to_s)
26
+ end
27
+
28
+ it 'should return nil for the template string if no file is specified' do
29
+ @useless_plugin.config_template_string.should be_nil
30
+ end
31
+
32
+ it 'should compile the template string for a HAML file' do
33
+ filepath = @another_plugin.config_template_file
34
+ haml = IO.read(filepath)
35
+ html = Haml::Engine.new(haml).render
36
+ @another_plugin.config_template_string.should == html
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,56 @@
1
+
2
+ module Locomotive
3
+ module Plugin
4
+ describe DBModels do
5
+
6
+ before(:each) do
7
+ plugin = PluginWithDBModel.new({})
8
+ plugin.build_visit_count(count: 5)
9
+ plugin.items.build(name: 'First Item')
10
+ plugin.items.build(name: 'Second Item')
11
+ plugin.save_db_model_container.should be_true
12
+
13
+ # Reload from the database
14
+ @plugin_with_db_model = PluginWithDBModel.new({})
15
+ end
16
+
17
+ it 'should persist DBModel items' do
18
+ @plugin_with_db_model.visit_count.count.should == 5
19
+
20
+ @plugin_with_db_model.items.count.should == 2
21
+ @plugin_with_db_model.items[0].name.should == 'First Item'
22
+ @plugin_with_db_model.items[1].name.should == 'Second Item'
23
+ end
24
+
25
+ it 'should allow mongoid queries on persisted DBModel items' do
26
+ @plugin_with_db_model.items.where(name: /First/).count.should == 1
27
+ @plugin_with_db_model.items.where(name: /First/).first.name.should == 'First Item'
28
+
29
+ @plugin_with_db_model.items.where(name: /Item/).count.should == 2
30
+ @plugin_with_db_model.items.where(name: /Item/)[0].name.should == 'First Item'
31
+ @plugin_with_db_model.items.where(name: /Item/)[1].name.should == 'Second Item'
32
+ end
33
+
34
+ it 'should embed DBModel items in a document for the plugin class' do
35
+ @plugin_with_db_model.visit_count.db_model_container.kind_of?(
36
+ PluginWithDBModel::DBModelContainer).should be_true
37
+ @plugin_with_db_model.items.first.db_model_container.kind_of?(
38
+ PluginWithDBModel::DBModelContainer).should be_true
39
+
40
+ @plugin_with_db_model.visit_count.relations[
41
+ 'db_model_container'].relation.should \
42
+ == Mongoid::Relations::Embedded::In
43
+ end
44
+
45
+ it 'should run all validations for DBModel items' do
46
+ @plugin_with_db_model.items.build(name: '')
47
+ @plugin_with_db_model.save_db_model_container.should be_false
48
+
49
+ @plugin_with_db_model.db_model_container.errors.messages.should == {
50
+ :items => [ 'is invalid' ]
51
+ }
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,125 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ module Locomotive
5
+ module Plugin
6
+ describe Liquid do
7
+
8
+ describe '#prefixed_liquid_filter_module' do
9
+
10
+ before(:each) do
11
+ @plugin_with_filter = PluginWithFilter.new({})
12
+ end
13
+
14
+ it 'should contain all prefixed methods for provided filter modules' do
15
+ mod = @plugin_with_filter.prefixed_liquid_filter_module('prefix')
16
+ mod.public_instance_methods.should include(:prefix_add_http)
17
+ end
18
+
19
+ it 'should not contain any of the original methods' do
20
+ mod = @plugin_with_filter.prefixed_liquid_filter_module('prefix')
21
+ mod.public_instance_methods.should_not include(:add_http)
22
+ end
23
+
24
+ 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'
29
+ end
30
+
31
+ it 'includes multiple filter modules for one plugin' do
32
+ @plugin_with_many_filter_modules = PluginWithManyFilterModules.new({})
33
+ mod = @plugin_with_many_filter_modules.prefixed_liquid_filter_module('prefix')
34
+ mod.public_instance_methods.should include(:prefix_add_newline)
35
+ mod.public_instance_methods.should include(:prefix_remove_http)
36
+ end
37
+
38
+ it 'works if multiple prefixed modules are mixed into the same object' do
39
+ @plugin_with_many_filter_modules = PluginWithManyFilterModules.new({})
40
+
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'))
44
+
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'
48
+ end
49
+
50
+ end
51
+
52
+ describe 'liquid tags' do
53
+
54
+ before(:each) do
55
+ @plugin_class = PluginWithTags
56
+ @prefixed_tags = @plugin_class.prefixed_liquid_tags('prefix')
57
+
58
+ @enabled_tags = []
59
+ @context = ::Liquid::Context.new
60
+ @context.registers[:enabled_plugin_tags] = @enabled_tags
61
+
62
+ @raw_template = <<-TEMPLATE
63
+ {% prefix_paragraph %}Some Text{% endprefix_paragraph %}
64
+ Some Text{% prefix_newline %}
65
+ TEMPLATE
66
+ end
67
+
68
+ it 'supplies the prefixed tag names along with subclasses of the tag classes' do
69
+ @prefixed_tags.size.should == 2
70
+ @prefixed_tags['prefix_paragraph'].should be < PluginWithTags::Paragraph
71
+ @prefixed_tags['prefix_newline'].should be < PluginWithTags::Newline
72
+ end
73
+
74
+ it 'only renders a tag if it is enabled in the liquid context' do
75
+ expected_output = <<-TEMPLATE
76
+ <p>Some Text</p>
77
+ Some Text<br />
78
+ TEMPLATE
79
+
80
+ register_tags(@prefixed_tags)
81
+ template = ::Liquid::Template.parse(@raw_template)
82
+ template.render(@context).should_not == expected_output
83
+
84
+ @enabled_tags << @prefixed_tags['prefix_paragraph']
85
+ @enabled_tags << @prefixed_tags['prefix_newline']
86
+ template.render(@context).should == expected_output
87
+ end
88
+
89
+ it 'uses render_disabled or empty string if the plugin is not enabled' do
90
+ expected_output = <<-TEMPLATE
91
+ Some Text
92
+ Some Text
93
+ TEMPLATE
94
+
95
+ register_tags(@prefixed_tags)
96
+ template = ::Liquid::Template.parse(@raw_template)
97
+ template.render(@context).should == expected_output
98
+ end
99
+
100
+ it 'uses render_disabled or empty string if no plugin is enabled' do
101
+ @context.registers.delete(:enabled_plugin_tags)
102
+
103
+ expected_output = <<-TEMPLATE
104
+ Some Text
105
+ Some Text
106
+ TEMPLATE
107
+
108
+ register_tags(@prefixed_tags)
109
+ template = ::Liquid::Template.parse(@raw_template)
110
+ template.render(@context).should == expected_output
111
+ end
112
+
113
+ protected
114
+
115
+ def register_tags(tags)
116
+ tags.each do |name, klass|
117
+ ::Liquid::Template.register_tag(name, klass)
118
+ end
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+ end
125
+ end