mvcli 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 843f2e9a35f848c28d8fdf547f31234e1ee1ebd9
4
- data.tar.gz: 0505a91108a5c2ea656d6c0b2d06307a812fa04b
3
+ metadata.gz: 79165f026197eb88758309a18703c91449f6849b
4
+ data.tar.gz: b2b3e3ee52b717fb962e3675e97cf16595fdc101
5
5
  SHA512:
6
- metadata.gz: fdbb170f92c985159aff0be98f9111e5bc38fb13bf71ce66a0cd23bc1e4b83bda7839d41fa51fe0545875469264c1c755343fcc659fd283a7e6ad4bacf16130c
7
- data.tar.gz: bb7efd8d702ac6d6d1b798c2659b5c7f09fb915bd11c4a320a84a285ca6b309204b3215171ab4714c6515efcdca92ce33683b1060cf2107d369ed95f33ebdd6b
6
+ metadata.gz: 3f48d494fbada3facb993260ceffeff544a16d0853ae3cdb73ffffd6af815f43ce5199058d525592885e553db16c60353ae1ba78845abdf1c2a5b4037a6067a7
7
+ data.tar.gz: 6c374042c711e0802bcfa3e287835a25b0471e4f56a2ac61d722f66aeb8cc58302369e9b5b2650921d727a075d6c9fdd8a715dc13cae5494cb3ccc55bef98030
data/lib/mvcli/actions.rb CHANGED
@@ -1,10 +1,11 @@
1
+ require "map"
1
2
  require_relative "loader"
2
3
  require_relative "renderer"
3
4
 
4
5
  module MVCLI
5
6
  class Actions
6
- def initialize(root, loader = nil, renderer = nil)
7
- @loader = loader || Loader.new(root)
7
+ def initialize(root, loader = Loader.new, renderer = nil)
8
+ @loader = loader
8
9
  @renderer = renderer || Renderer.new(root)
9
10
  end
10
11
 
@@ -21,8 +22,8 @@ module MVCLI
21
22
  @method = method
22
23
  end
23
24
 
24
- def call(command)
25
- controller = @loader.load :controller, @controller
25
+ def call(command, bindings = Map.new)
26
+ controller = @loader.load :controller, @controller, bindings
26
27
  context = controller.send @method
27
28
  path = [@controller, @method].join('/')
28
29
  @renderer.render command.output, path, context
data/lib/mvcli/app.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "mvcli"
1
2
  require_relative "middleware"
2
3
  require_relative "command"
3
4
  require_relative "actions"
@@ -10,6 +11,7 @@ module MVCLI
10
11
  @router = Router.new Actions.new root
11
12
  @router.instance_eval route_file.read, route_file.to_s, 1
12
13
  ActiveSupport::Dependencies.autoload_paths << root.join('app/providers')
14
+ ActiveSupport::Dependencies.autoload_paths << root.join('app/controllers')
13
15
  @middleware = Middleware.new
14
16
  @middleware << Provisioning::Middleware.new
15
17
  @middleware << @router
@@ -0,0 +1,7 @@
1
+ class MVCLI::Controller
2
+ attr_reader :params
3
+
4
+ def initialize(params = {})
5
+ @params = params
6
+ end
7
+ end
data/lib/mvcli/loader.rb CHANGED
@@ -1,15 +1,11 @@
1
+ require "active_support/inflector/methods"
2
+
1
3
  module MVCLI
2
4
  class Loader
3
- def initialize(path)
4
- @path = path
5
- end
5
+ include ActiveSupport::Inflector
6
6
 
7
- def load(type, name)
8
- pathname = "#{name}_#{type}"
9
- filename = File.join(@path, "app/#{type}s", pathname)
10
- require filename
11
- classname = pathname.capitalize.gsub(/_(\w)/) {|m| m[1].upcase}
12
- Object.const_get(classname).new
7
+ def load(type, name, *args, &block)
8
+ constantize(camelize("#{name}_#{type}")).new(*args, &block)
13
9
  end
14
10
  end
15
11
  end
@@ -1,6 +1,7 @@
1
1
  require "map"
2
2
  require "active_support/concern"
3
3
  require "active_support/dependencies"
4
+ require "mvcli/loader"
4
5
 
5
6
  module MVCLI
6
7
  module Provisioning
@@ -51,8 +52,11 @@ module MVCLI
51
52
  end
52
53
 
53
54
  class Provisioner
55
+ def initialize
56
+ @loader = Loader.new
57
+ end
54
58
  def [](name)
55
- provider = "#{name.capitalize}Provider".constantize.new
59
+ provider = @loader.load :provider, name
56
60
  provider.value
57
61
  end
58
62
  end
@@ -0,0 +1,90 @@
1
+ require "map"
2
+
3
+ module MVCLI
4
+ class Router
5
+ class Pattern
6
+ def initialize(pattern)
7
+ @matchers = compile pattern
8
+ end
9
+
10
+ def match(input, consumed = [], matchers = @matchers, satisfied = [], bindings = Map.new)
11
+ matcher, *unsatisfied = *matchers
12
+ value, *rest = *input
13
+ unless matcher && value && matcher.matches?(value)
14
+ Match.new input, consumed, matchers, satisfied, bindings
15
+ else
16
+ match rest, consumed + [value], unsatisfied, satisfied + [matcher], bindings.merge(matcher.bind(value))
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def compile(pattern)
23
+ pattern.strip.split(/\s+/).map {|segment| Matcher[segment]}
24
+ end
25
+
26
+ class Matcher
27
+
28
+ def self.[](segment)
29
+ case segment
30
+ when /^:(\w+)/ then Variable.new($1)
31
+ else
32
+ Literal.new(segment)
33
+ end
34
+ end
35
+
36
+ def initialize(name)
37
+ @name = name
38
+ end
39
+
40
+ def bind(input)
41
+ {}
42
+ end
43
+
44
+ class Variable < Matcher
45
+ def matches?(input)
46
+ true
47
+ end
48
+
49
+ def bind(input)
50
+ {@name => input}
51
+ end
52
+ end
53
+
54
+ class Literal < Matcher
55
+ def matches?(input)
56
+ input == @name
57
+ end
58
+ end
59
+ end
60
+
61
+ class Match
62
+ attr_reader :bindings
63
+
64
+ def initialize(input, consumed, unsatisfied, satisfied, bindings)
65
+ @unsatisfied = unsatisfied
66
+ @satisfied = satisfied
67
+ @input = input
68
+ @consumed = consumed
69
+ @bindings = bindings
70
+ end
71
+
72
+ def satisfied?
73
+ @unsatisfied.empty?
74
+ end
75
+
76
+ def exhaustive?
77
+ @input.empty?
78
+ end
79
+
80
+ def matches?
81
+ satisfied? && exhaustive?
82
+ end
83
+
84
+ def partial?
85
+ !@consumed.empty?
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
data/lib/mvcli/router.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "map"
2
+ require_relative "router/pattern"
2
3
 
3
4
  module MVCLI
4
5
  class Router
@@ -17,29 +18,33 @@ module MVCLI
17
18
  end
18
19
 
19
20
  def call(command)
20
- if route = @routes.find {|r| r.matches? command}
21
- return route.call command
21
+ @routes.each do |route|
22
+ if match = route.match(command)
23
+ return match.call command
24
+ end
22
25
  end
23
26
  fail RoutingError, "no route matches '#{command.argv.join ' '}'"
24
27
  end
25
28
 
26
29
  class Route
27
30
  def initialize(pattern, actions, action, options = {})
28
- @pattern, @actions, @action, @options = pattern.to_s, actions, action, options
31
+ @pattern = Pattern.new pattern.to_s
32
+ @actions, @action, @options = actions, action, options
29
33
  end
30
34
 
31
- def matches?(command)
32
- segments = @pattern.split /\s+/
33
- segments.each_with_index do |s, i|
34
- return false unless command.argv[i] && s.to_s == command.argv[i]
35
+ def match(command)
36
+ match = @pattern.match(command.argv)
37
+ if match.matches?
38
+ proc do |command|
39
+ action = @actions[@action] or fail "no action found for #{@action}"
40
+ action.call command, match.bindings
41
+ end
35
42
  end
36
- return true
37
43
  end
44
+ end
45
+
46
+ class Match
38
47
 
39
- def call(command)
40
- action = @actions[@action] or fail "no action found for #{@action}"
41
- action.call command
42
- end
43
48
  end
44
49
  end
45
50
  end
data/lib/mvcli/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module MVCLI
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/mvcli.rb CHANGED
@@ -1,5 +1,4 @@
1
- require "mvcli/version"
2
-
3
1
  module MVCLI
4
- # Your code goes here...
2
+ require "mvcli/version"
3
+ require "mvcli/controller"
5
4
  end
@@ -20,9 +20,9 @@ describe "MVCLI::Actions" do
20
20
  context "when the class exists" do
21
21
  Given(:output) {mock(:Output)}
22
22
  Given(:controller) {mock(:Controller)}
23
- Given {loader.stub(:load).with(:controller, 'foo') {controller}}
23
+ Given {loader.stub(:load).with(:controller, 'foo', {}) {controller}}
24
24
  Given {controller.stub(:bar) {"the context"}}
25
- When {actions['foo#bar'].call(mock(:Command, :output => output))}
25
+ When {actions['foo#bar'].call(mock(:Command, :output => output), {})}
26
26
  Then {controller.should have_received(:bar)}
27
27
  And {renderer.should have_received(:render).with(output, 'foo/bar', 'the context')}
28
28
  end
@@ -2,12 +2,22 @@ require "spec_helper"
2
2
  require "mvcli/loader"
3
3
 
4
4
  describe "MVCLI::Loader" do
5
+ Given do
6
+ class ::TotallyAwesomeController
7
+ attr_reader :params
8
+ attr_reader :block
9
+ def initialize(params, &block)
10
+ @params = params
11
+ @block = block
12
+ end
13
+ end
14
+ end
15
+ Given(:loader) {MVCLI::Loader.new}
5
16
 
6
- Given {class ::TotallyAwesomeController; end}
7
- Given(:loader) {MVCLI::Loader.new '/path/to/app'}
8
- Given {loader.stub(:require) {true}}
9
- context "loading a controller in the global namespace" do
10
- When(:controller){loader.load :controller, 'totally_awesome'}
17
+ context "when a controller load is requested in the global namespace" do
18
+ When(:controller){loader.load(:controller, 'totally_awesome', {:foo => :bar}) {'whee!'}}
11
19
  Then {controller.should be_instance_of TotallyAwesomeController}
20
+ And {controller.params.should eql ({:foo => :bar})}
21
+ And {controller.block.call.should eql "whee!"}
12
22
  end
13
23
  end
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+ require "mvcli/router/pattern"
3
+
4
+ describe "MVCLI::Router::Pattern" do
5
+ use_natural_assertions
6
+ describe "with a simple input" do
7
+ Given(:pattern) {compile "one two three"}
8
+ context "when matched against unrelated content" do
9
+ When(:match) {pattern.match %w(five six seven)}
10
+ Then {!match.matches?}
11
+ And {!match.exhaustive?}
12
+ And {!match.partial?}
13
+ And {!match.satisfied?}
14
+ end
15
+ context "when matched against the same content" do
16
+ When(:match) {pattern.match %w(one two three)}
17
+ Then {match.matches?}
18
+ And {match.exhaustive?}
19
+ And {match.partial?}
20
+ And {match.satisfied?}
21
+
22
+ end
23
+ context "when matched against partially matching content" do
24
+ When(:match) {pattern.match %w(one two)}
25
+ Then {match.partial?}
26
+ And {match.exhaustive?}
27
+ end
28
+ context "on an unexhausted sequence" do
29
+ When(:match) {pattern.match %w(one two three four)}
30
+ Then {!match.exhaustive?}
31
+ And {match.partial?}
32
+ end
33
+ end
34
+ describe "with variables" do
35
+ Given(:pattern) {compile "one :one :two two "}
36
+ context "on a matching sequence" do
37
+ When(:match) {pattern.match %w(one 1 2 two)}
38
+ Then {match.matches?}
39
+ And {match.bindings[:one] == '1'}
40
+ And {match.bindings[:two] = '2'}
41
+ end
42
+ context "on a partially matching sequence" do
43
+ When(:match) {pattern.match %w(one 1)}
44
+ Then {match.partial?}
45
+ And {!match.matches?}
46
+ And {match.bindings[:one] == "1"}
47
+ And {match.bindings[:two] == nil}
48
+ end
49
+ end
50
+
51
+ def compile(*args)
52
+ MVCLI::Router::Pattern.new(*args)
53
+ end
54
+ end
@@ -5,8 +5,12 @@ describe "MVCLI::Router" do
5
5
  Given(:Router) {MVCLI::Router}
6
6
  Given(:actions) {mock(:Actions)}
7
7
  Given(:router) {self.Router.new actions}
8
- Given {actions.stub(:[]) {|action| @action = action; ->(command) {@command = command}}}
9
-
8
+ Given do
9
+ actions.stub(:[]) do |action|
10
+ @action = action
11
+ ->(command, bindings) {@command = command; @bindings = bindings}
12
+ end
13
+ end
10
14
 
11
15
  context "without any routes" do
12
16
  When(:result) {invoke}
@@ -27,12 +31,15 @@ describe "MVCLI::Router" do
27
31
  Then {@command.argv.should eql ['bam']}
28
32
  end
29
33
 
30
- context "with a route with dynamic segments" do
31
- Given {router.match 'show loadbalancer :name' => 'loadbalancers#show'}
32
- When(:result) {invoke 'show loadbalancer 6'}
34
+ context "with a route with captures" do
35
+ Given {router.match 'show loadbalancer :id' => 'loadbalancers#show'}
36
+ When {invoke 'show loadbalancer 6'}
37
+ Then {@action.should eql 'loadbalancers#show'}
38
+ And {@command.argv == ['show' 'loadbalancer' '6']}
39
+ And {@bindings[:id].should eql '6'}
33
40
  end
34
41
 
35
- def invoke(*args)
36
- router.call mock(:Command, :argv => args)
42
+ def invoke(route = '')
43
+ router.call mock(:Command, :argv => route.split(/\s+/))
37
44
  end
38
45
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mvcli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Lowell
@@ -29,7 +29,7 @@ cert_chain:
29
29
  UgImJlChAzCoDP9zi9tdm6jAr7ttF25R9PPYr11ILb7dYe3qUzlNlM6zJx/nb31b
30
30
  IhdyRVup4qLcqYSTPsm6u7VA
31
31
  -----END CERTIFICATE-----
32
- date: 2013-05-25 00:00:00.000000000 Z
32
+ date: 2013-05-28 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: map
@@ -77,12 +77,14 @@ files:
77
77
  - lib/mvcli/actions.rb
78
78
  - lib/mvcli/app.rb
79
79
  - lib/mvcli/command.rb
80
+ - lib/mvcli/controller.rb
80
81
  - lib/mvcli/erb.rb
81
82
  - lib/mvcli/loader.rb
82
83
  - lib/mvcli/middleware.rb
83
84
  - lib/mvcli/provisioning.rb
84
85
  - lib/mvcli/renderer.rb
85
86
  - lib/mvcli/router.rb
87
+ - lib/mvcli/router/pattern.rb
86
88
  - lib/mvcli/version.rb
87
89
  - mvcli.gemspec
88
90
  - spec/mvcli/actions_spec.rb
@@ -91,6 +93,7 @@ files:
91
93
  - spec/mvcli/loader_spec.rb
92
94
  - spec/mvcli/middleware_spec.rb
93
95
  - spec/mvcli/provisioning_spec.rb
96
+ - spec/mvcli/router/pattern_spec.rb
94
97
  - spec/mvcli/router_spec.rb
95
98
  - spec/spec_helper.rb
96
99
  homepage: https://github.com/cowboyd/mvcli
@@ -124,5 +127,6 @@ test_files:
124
127
  - spec/mvcli/loader_spec.rb
125
128
  - spec/mvcli/middleware_spec.rb
126
129
  - spec/mvcli/provisioning_spec.rb
130
+ - spec/mvcli/router/pattern_spec.rb
127
131
  - spec/mvcli/router_spec.rb
128
132
  - spec/spec_helper.rb