locomotive_plugins 1.0.0.beta7 → 1.0.0.beta8

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -283,10 +283,16 @@ single site, since each site will have its own database.
283
283
  ### Rack App
284
284
 
285
285
  Plugins can supply a Rack Application to be used for request handling. Do so by
286
- overriding the `rack_app` class method on the plugin class. The plugin object
287
- can also build URLs and paths for that Rack App without knowledge of where it
288
- is mounted. This is important because Locomotive will mount the rack
289
- application on a path based on the plugin's `plugin_id`. Given a string `path`
290
- which is a path relative to the root of the rack app,
291
- `plugin_object.full_path(path)` will give the full absolute path and
292
- `plugin_object.full_url(path)` will give the full URL.
286
+ overriding the `rack_app` class method on the plugin class. The Rack app will
287
+ be given some helper methods:
288
+
289
+ * `plugin_object`: retrieve the plugin object.
290
+ * `full_path(path)`: generate the full url path for `path`. The `path` variable
291
+ is a url path relative to the mountpoint of the Rack app.
292
+ * `full_url(path)`: generate the full url for `path`. The `path` variable is a
293
+ url path relative to the mountpoint of the Rack app.
294
+
295
+ The `full_path` and `full_url` helpers may be used by the Rack app to generate
296
+ full paths and urls without explicit knowledge of the Rack app's mountpoint.
297
+ This is important since Locomotive will mount the app to a path based on its
298
+ `plugin_id`.
@@ -23,7 +23,6 @@ module Locomotive
23
23
  # @param base the plugin class
24
24
  def self.included(base)
25
25
  self.add_liquid_class_methods(base)
26
- self.add_rack_app_helper_methods(base)
27
26
 
28
27
  base.class_eval do
29
28
  extend ActiveModel::Callbacks
@@ -5,86 +5,97 @@ module Locomotive
5
5
  # `rack_app` method.
6
6
  module RackAppHelpers
7
7
 
8
- # @private
9
- #
10
- # Add class methods.
11
- #
12
- # @param base the class which includes this module
13
- def self.included(base)
14
- base.extend(ClassMethods)
15
- end
8
+ # Helper methods to be added to the Rack application.
9
+ module HelperMethods
10
+ attr_accessor :plugin_object
11
+ attr_reader :env
16
12
 
17
- # @api internal
18
- module ClassMethods
19
- # Adds methods from RackAppHelpersClassMethods module.
13
+ # Set the env on the Rack app so that it can be retrieved later. This
14
+ # method will yield, and then set the env back to what it was after the
15
+ # block returns.
20
16
  #
21
- # @param base the plugin class to extend LiquidClassMethods
22
- def add_rack_app_helper_methods(base)
23
- base.extend(RackAppHelpersClassMethods)
17
+ # @param env the Rack environment
18
+ def with_env(env)
19
+ old_env = @env
20
+ @env = env
21
+ yield
22
+ ensure
23
+ @env = old_env
24
24
  end
25
- end
26
25
 
27
- # This module adds class methods for managing the mount point for the
28
- # Rack App. The mount point must be set in order to properly generate
29
- # paths and URLs.
30
- module RackAppHelpersClassMethods
31
- # Set the full URL mountpoint for the Rack app. This will be set by
32
- # Locomotive CMS. The URL must be an HTTP or HTTPS url with no query
33
- # parameters or hash fragments.
26
+ # Generate the full absolute path for the given path based on the
27
+ # mountpoint of this plugin's rack app.
34
28
  #
35
- # @param url [String] the full URL that the Rack app is mounted on
36
- def set_mountpoint(url)
37
- error = lambda do |msg|
38
- raise Error, "Invalid mountpoint: #{msg}"
39
- end
29
+ # @param path [String] the path relative to the mountpoint of the rack
30
+ # app
31
+ # @return the absolute path
32
+ def full_path(path)
33
+ [
34
+ base_uri_object.path.sub(%r{/+$}, ''),
35
+ path.sub(%r{^/+}, '')
36
+ ].join('/')
37
+ end
40
38
 
41
- uri = URI(url)
42
- unless uri.scheme =~ /^https?$/
43
- error.call('only http or https allowed')
44
- end
45
- if uri.fragment
46
- error.call('no hash fragment allowed')
47
- end
48
- if uri.query
49
- error.call('no query string allowed')
50
- end
39
+ # Generate the full URL for the given path based on the mountpoint of
40
+ # this plugin's rack app.
41
+ #
42
+ # @param path [String] the path relative to the mountpoint of the rack
43
+ # app
44
+ # @return the URL
45
+ def full_url(path)
46
+ [
47
+ base_uri_object.to_s.sub(%r{/+$}, ''),
48
+ path.sub(%r{^/+}, '')
49
+ ].join('/')
50
+ end
51
51
 
52
- @mountpoint = uri
52
+ protected
53
+
54
+ def base_uri_object
55
+ request_uri = env['REQUEST_URI']
56
+ base_path = env['SCRIPT_NAME']
57
+ base_uri = request_uri.sub(/(#{base_path}).*$/, '\1')
58
+ URI(base_uri)
53
59
  end
60
+ end
54
61
 
55
- # Get the mountpoint for this plugin class's Rack app.
62
+ # Wrapper class around the Rack application returned by the plugin class.
63
+ # Acts as middleware to ensure some setup and teardown when the app is
64
+ # called.
65
+ class RackAppWrapper
66
+ # Initialize with the Rack application to wrap.
56
67
  #
57
- # @return the mountpoint
58
- def mountpoint
59
- @mountpoint
68
+ # @param app the Rack application
69
+ def initialize(app)
70
+ @app = app
60
71
  end
61
- end
62
72
 
63
- # Generate the full absolute path for the given path based on the
64
- # mountpoint of this plugin's rack app.
65
- #
66
- # @param path [String] the path relative to the mountpoint of the rack
67
- # app
68
- # @return the absolute path
69
- def full_path(path)
70
- [ mountpoint.path.sub(%r{/+$}, ''), path.sub(%r{^/+}, '') ].join('/')
73
+ # Call the underlying Rack app with the given environment.
74
+ #
75
+ # @param env the Rack environment
76
+ def call(env)
77
+ @app.with_env(env) do
78
+ @app.call(env)
79
+ end
80
+ end
71
81
  end
72
82
 
73
- # Generate the full URL for the given path based on the mountpoint of
74
- # this plugin's rack app.
83
+ # Adds helper methods to the Rack app and returns another Rack app which
84
+ # wraps it. This method gets the Rack application ready to be called by
85
+ # Locomotive. Locomotive CMS calls this method to get the Rack app rather
86
+ # than calling `rack_app` directly.
75
87
  #
76
- # @param path [String] the path relative to the mountpoint of the rack
77
- # app
78
- # @return the URL
79
- def full_url(path)
80
- [ mountpoint.to_s.sub(%r{/+$}, ''), path.sub(%r{^/+}, '') ].join('/')
81
- end
88
+ # @return the Rack app with helper methods
89
+ def prepared_rack_app
90
+ app = self.class.rack_app
82
91
 
83
- # Get the mountpoint for this plugin's Rack app.
84
- #
85
- # @return the mountpoint
86
- def mountpoint
87
- self.class.mountpoint
92
+ # Extend helper module if needed
93
+ unless app.singleton_class.included_modules.include?(HelperMethods)
94
+ app.extend(HelperMethods)
95
+ end
96
+
97
+ app.plugin_object = self
98
+ RackAppWrapper.new(app)
88
99
  end
89
100
 
90
101
  end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LocomotivePlugins
2
- VERSION = '1.0.0.beta7'
2
+ VERSION = '1.0.0.beta8'
3
3
  end
@@ -5,60 +5,96 @@ module Locomotive
5
5
  module Plugin
6
6
  describe RackAppHelpers do
7
7
 
8
- before(:all) do
9
- PluginWithRackApp.set_mountpoint('http://www.example.com/my/path')
8
+ let(:config) { {} }
9
+
10
+ let(:plugin) { PluginWithRackApp.new(config) }
11
+
12
+ let(:prepared_app) { plugin.prepared_rack_app }
13
+
14
+ let(:original_app) { plugin.class.rack_app }
15
+
16
+ it 'should add the plugin object to the Rack app' do
17
+ stub_app_call do
18
+ original_app.plugin_object.should == plugin
19
+ end
20
+
21
+ prepared_app.call(default_env)
10
22
  end
11
23
 
12
- before(:each) do
13
- @config = {}
14
- @plugin = PluginWithRackApp.new(@config)
24
+ it 'should add the Rack environment to the Rack app' do
25
+ stub_app_call do
26
+ original_app.env.should == default_env
27
+ end
28
+
29
+ prepared_app.call(default_env)
15
30
  end
16
31
 
17
- it 'should supply an absolute path based on where it is mounted' do
18
- @plugin.full_path('/plugin/path').should == '/my/path/plugin/path'
19
- @plugin.full_path('another//path').should == '/my/path/another//path'
32
+ it 'should add path and url helpers to the Rack app' do
33
+ original_app.respond_to?(:full_path).should be_true
34
+ original_app.respond_to?(:full_url).should be_true
35
+ end
36
+
37
+ it 'should not add the helper methods if they have already been added' do
38
+ NewRackAppClass = Class.new do
39
+ def method_missing(*args)
40
+ end
41
+ end
42
+
43
+ plugin = PluginWithRackApp.new(config)
44
+ rack_app = NewRackAppClass.new
45
+ plugin.class.stubs(:rack_app).returns(rack_app)
46
+
47
+ rack_app.expects(:extend).with(HelperMethods)
48
+ app = plugin.prepared_rack_app
49
+
50
+ plugin = PluginWithRackApp.new(config)
51
+ rack_app = NewRackAppClass.new
52
+ plugin.class.stubs(:rack_app).returns(rack_app)
53
+
54
+ app = plugin.prepared_rack_app
55
+ rack_app.expects(:extend).with(HelperMethods).never
56
+ app = plugin.prepared_rack_app
20
57
  end
21
58
 
22
- it 'should supply a full URL based on where it is mounted' do
23
- @plugin.full_url('/plugin/path').should ==
24
- 'http://www.example.com/my/path/plugin/path'
25
- @plugin.full_url('another//path').should ==
26
- 'http://www.example.com/my/path/another//path'
59
+ it 'should supply an absolute path' do
60
+ stub_app_call do
61
+ original_app.full_path('/plugin/path').should == '/my/path/plugin/path'
62
+ original_app.full_path('another//path').should == '/my/path/another//path'
63
+ end
64
+
65
+ prepared_app.call(default_env)
27
66
  end
28
67
 
29
- it 'should only allow URLs with proper format' do
30
- old_mountpoint = PluginWithRackApp.mountpoint
31
-
32
- bad_mountpoints = [
33
- 'my/path',
34
- 'my.server.com/my/path',
35
- 'ftp://my.server.com',
36
- 'http://my.server.com/my/path?q=value',
37
- 'http://my.server.com/my/path#fragment',
38
- 'https://my.server.com/my/path?q=value',
39
- 'https://my.server.com/my/path#fragment'
40
- ]
41
-
42
- good_mountpoints = [
43
- 'http://my.server.com/my/path',
44
- 'http://my.server.com:3000/my/path',
45
- 'https://my.server.com/my/path',
46
- 'https://my.server.com:3000/my/path'
47
- ]
48
-
49
- bad_mountpoints.each do |mountpoint|
50
- lambda do
51
- PluginWithRackApp.set_mountpoint(mountpoint)
52
- end.should raise_exception(Locomotive::Plugin::Error)
53
- PluginWithRackApp.mountpoint.should == old_mountpoint
68
+ it 'should supply a full url' do
69
+ stub_app_call do
70
+ original_app.full_url('/plugin/path').should ==
71
+ 'http://www.example.com/my/path/plugin/path'
72
+ original_app.full_url('another//path').should ==
73
+ 'http://www.example.com/my/path/another//path'
54
74
  end
55
75
 
56
- good_mountpoints.each do |mountpoint|
57
- lambda do
58
- PluginWithRackApp.set_mountpoint(mountpoint)
59
- end.should_not raise_exception
60
- PluginWithRackApp.mountpoint.to_s.should == mountpoint
76
+ prepared_app.call(default_env)
77
+ end
78
+
79
+ protected
80
+
81
+ def default_env
82
+ {
83
+ 'REQUEST_URI' => 'http://www.example.com/my/path/request/path',
84
+ 'SCRIPT_NAME' => '/my/path'
85
+ }
86
+ end
87
+
88
+ # Sets up an object which expects the method `the_block_has_been_called`
89
+ # to be invoked. This way, if you stub the app call but do not call
90
+ # `prepared_app.call`, the spec will fail.
91
+ def stub_app_call(&block)
92
+ obj = Object.new
93
+ original_app.block = Proc.new do
94
+ obj.the_block_has_been_called
95
+ block.call
61
96
  end
97
+ obj.expects(:the_block_has_been_called).at_least_once
62
98
  end
63
99
 
64
100
  end
@@ -3,7 +3,16 @@ class PluginWithRackApp
3
3
  include Locomotive::Plugin
4
4
 
5
5
  def self.rack_app
6
- Proc.new do
6
+ RackApp
7
+ end
8
+
9
+ class RackApp
10
+ class << self
11
+ attr_accessor :block
12
+ end
13
+
14
+ def self.call(env)
15
+ block.call
7
16
  [200, {'Content-Type' => 'text/html'}, []]
8
17
  end
9
18
  end
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.beta7
4
+ version: 1.0.0.beta8
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: 2013-01-24 00:00:00.000000000 Z
12
+ date: 2013-01-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: locomotive_liquid