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 +4 -4
- data/lib/mvcli/actions.rb +5 -4
- data/lib/mvcli/app.rb +2 -0
- data/lib/mvcli/controller.rb +7 -0
- data/lib/mvcli/loader.rb +5 -9
- data/lib/mvcli/provisioning.rb +5 -1
- data/lib/mvcli/router/pattern.rb +90 -0
- data/lib/mvcli/router.rb +17 -12
- data/lib/mvcli/version.rb +1 -1
- data/lib/mvcli.rb +2 -3
- data/spec/mvcli/actions_spec.rb +2 -2
- data/spec/mvcli/loader_spec.rb +15 -5
- data/spec/mvcli/router/pattern_spec.rb +54 -0
- data/spec/mvcli/router_spec.rb +14 -7
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79165f026197eb88758309a18703c91449f6849b
|
4
|
+
data.tar.gz: b2b3e3ee52b717fb962e3675e97cf16595fdc101
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 =
|
7
|
-
@loader = loader
|
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
|
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
|
-
|
4
|
-
@path = path
|
5
|
-
end
|
5
|
+
include ActiveSupport::Inflector
|
6
6
|
|
7
|
-
def load(type, name)
|
8
|
-
|
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
|
data/lib/mvcli/provisioning.rb
CHANGED
@@ -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 =
|
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
|
-
|
21
|
-
|
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
|
31
|
+
@pattern = Pattern.new pattern.to_s
|
32
|
+
@actions, @action, @options = actions, action, options
|
29
33
|
end
|
30
34
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
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
data/lib/mvcli.rb
CHANGED
data/spec/mvcli/actions_spec.rb
CHANGED
@@ -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
|
data/spec/mvcli/loader_spec.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
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
|
data/spec/mvcli/router_spec.rb
CHANGED
@@ -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
|
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
|
31
|
-
Given {router.match 'show loadbalancer :
|
32
|
-
When
|
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(
|
36
|
-
router.call mock(:Command, :argv =>
|
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.
|
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-
|
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
|