mvcli 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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