mvcli 0.0.16 → 0.1.0

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +4 -0
  3. data/Gemfile +2 -2
  4. data/README.md +6 -2
  5. data/app.rb +7 -0
  6. data/app/routes.rb +0 -0
  7. data/bin/mvcli +5 -0
  8. data/lib/mvcli.rb +1 -0
  9. data/lib/mvcli/action.rb +31 -0
  10. data/lib/mvcli/app.rb +23 -28
  11. data/lib/mvcli/controller.rb +20 -2
  12. data/lib/mvcli/core.rb +101 -0
  13. data/lib/mvcli/cortex.rb +38 -0
  14. data/lib/mvcli/form.rb +13 -0
  15. data/lib/mvcli/loader.rb +45 -1
  16. data/lib/mvcli/middleware.rb +13 -15
  17. data/lib/mvcli/path.rb +41 -0
  18. data/lib/mvcli/plugins.rb +19 -0
  19. data/lib/mvcli/plugins/controllers/plugins_controller.rb +23 -0
  20. data/lib/mvcli/plugins/forms/plugins/install_form.rb +6 -0
  21. data/lib/mvcli/plugins/models/plugins/installation_model.rb +33 -0
  22. data/lib/mvcli/plugins/providers/bundle_provider.rb +116 -0
  23. data/lib/mvcli/plugins/routes.rb +3 -0
  24. data/lib/mvcli/plugins/templates/plugins/index.txt.erb +3 -0
  25. data/lib/mvcli/plugins/templates/plugins/install.txt.erb +1 -0
  26. data/lib/mvcli/plugins/templates/plugins/uninstall.txt.erb +1 -0
  27. data/lib/mvcli/provisioning.rb +48 -38
  28. data/lib/mvcli/router.rb +34 -20
  29. data/lib/mvcli/std/extensions/erb_extension.rb +24 -0
  30. data/lib/mvcli/std/providers/argv_provider.rb +9 -0
  31. data/lib/mvcli/std/providers/config_provider.rb +29 -0
  32. data/lib/mvcli/std/providers/middleware_provider.rb +12 -0
  33. data/lib/mvcli/std/providers/router_provider.rb +17 -0
  34. data/lib/mvcli/std/routes.rb +2 -0
  35. data/lib/mvcli/version.rb +1 -1
  36. data/spec/features/managing_plugins_spec.rb +29 -0
  37. data/spec/fixtures/apps/trivium/app.rb +10 -0
  38. data/spec/fixtures/apps/trivium/app/routes.rb +0 -0
  39. data/spec/fixtures/apps/trivium/bin/trivium +6 -0
  40. data/spec/fixtures/apps/trivium/lib/trivium/version.rb +3 -0
  41. data/spec/fixtures/apps/trivium/trivium.gemspec +8 -0
  42. data/spec/fixtures/bin/trivium +3 -0
  43. data/spec/fixtures/plugins/timing-plugin/app/routes.rb +1 -0
  44. data/spec/fixtures/plugins/timing-plugin/lib/trivium-timing.rb +5 -0
  45. data/spec/fixtures/plugins/timing-plugin/trivium-timing.gemspec +9 -0
  46. data/spec/mvcli/action_spec.rb +31 -0
  47. data/spec/mvcli/controller_spec.rb +35 -0
  48. data/spec/mvcli/core_spec.rb +77 -0
  49. data/spec/mvcli/cortex_spec.rb +50 -0
  50. data/spec/mvcli/erb_spec.rb +1 -1
  51. data/spec/mvcli/form_spec.rb +1 -1
  52. data/spec/mvcli/loader_spec.rb +58 -13
  53. data/spec/mvcli/middleware/exception_logger_spec.rb +3 -3
  54. data/spec/mvcli/middleware/exit_status_spec.rb +10 -10
  55. data/spec/mvcli/middleware_spec.rb +28 -45
  56. data/spec/mvcli/path_spec.rb +13 -0
  57. data/spec/mvcli/path_spec/does/exist +1 -0
  58. data/spec/mvcli/plugins/providers/bundle_provider_spec.rb +23 -0
  59. data/spec/mvcli/provisioning_spec.rb +39 -37
  60. data/spec/mvcli/router_spec.rb +26 -32
  61. data/spec/mvcli/std/extensions/erb_extension_spec.rb +34 -0
  62. data/spec/mvcli/std/providers/middleware_provider_spec.rb +10 -0
  63. data/spec/mvcli/validatable_spec.rb +12 -12
  64. data/spec/spec_helper.rb +13 -1
  65. data/spec/support/aruba_helper.rb +76 -0
  66. metadata +69 -33
  67. data/lib/mvcli/actions.rb +0 -37
  68. data/lib/mvcli/renderer.rb +0 -16
  69. data/spec/mvcli/actions_spec.rb +0 -34
  70. data/spec/mvcli/dummy/app/providers/test_provider.rb +0 -5
@@ -4,10 +4,10 @@ require "mvcli/middleware/exception_logger"
4
4
  describe "MVCLI::Middleware::ExceptionLogger" do
5
5
  use_natural_assertions
6
6
 
7
- Given(:command) {mock(:Command, :log => "")}
8
- Given(:logger) {MVCLI::Middleware::ExceptionLogger.new}
7
+ Given(:command) { double(:Command, :log => "") }
8
+ Given(:logger) { MVCLI::Middleware::ExceptionLogger.new }
9
9
  context "with a cleanly running application" do
10
- When(:result) {logger.call(command) {0}}
10
+ When(:result) { logger.call(command) {0} }
11
11
  Then {result == 0}
12
12
  end
13
13
  context "with an app that raises an exception" do
@@ -3,24 +3,24 @@ require "mvcli/middleware/exit_status"
3
3
 
4
4
  describe MVCLI::Middleware::ExitStatus do
5
5
  use_natural_assertions
6
- Given(:command) {mock(:Command)}
7
- Given(:middleware) {MVCLI::Middleware::ExitStatus.new}
6
+ Given(:command) { double(:Command) }
7
+ Given(:middleware) { MVCLI::Middleware::ExitStatus.new }
8
8
  context "when called with code that succeeds" do
9
- When(:status) {middleware.call(command) {0}}
10
- Then {status == 0}
9
+ When(:status) { middleware.call(command) {0} }
10
+ Then { status == 0 }
11
11
  end
12
12
  context "when called with an app that fails with an exit status of 99" do
13
- When(:status) {middleware.call(command) {99}}
14
- Then{ status == 99}
13
+ When(:status) { middleware.call(command) {99} }
14
+ Then { status == 99 }
15
15
  end
16
16
 
17
17
  context "when the upstream app yields a non-integer" do
18
- When(:status) {middleware.call(command) {"whoopeee!"}}
19
- Then {status == 0}
18
+ When(:status) { middleware.call(command) {"whoopeee!"} }
19
+ Then { status == 0 }
20
20
  end
21
21
 
22
22
  context "when the upstream app raises an exception" do
23
- When(:status) {middleware.call(command) {fail "boom!"}}
24
- Then {status == 70}
23
+ When(:status) { middleware.call(command) {fail "boom!"} }
24
+ Then { status == 70 }
25
25
  end
26
26
  end
@@ -2,73 +2,56 @@ require "spec_helper"
2
2
  require "mvcli/middleware"
3
3
 
4
4
  describe "MVCLI::Middleware" do
5
- before do
6
- @middleware = MVCLI::Middleware.new
7
- @command = mock(:Command)
8
- end
5
+ use_natural_assertions
6
+ Given(:middleware) { MVCLI::Middleware.new }
7
+ Given(:command) { double :Command }
8
+
9
+ When { @result = middleware.call command }
9
10
 
10
- it "runs perfectly fine without any apps" do
11
- @middleware.call(@command).should eql 0
11
+ context "without any apps" do
12
+ Then { @result == 0 }
12
13
  end
13
14
 
14
- describe "with a single app" do
15
- before do
16
- @app = proc do |command|
15
+ context "with a single app" do
16
+ Given do
17
+ middleware << proc do |command|
17
18
  @called = command
18
19
  end
19
- @middleware << @app
20
- @middleware.call @command
21
- end
22
- it "calls it" do
23
- @called.should eql @command
24
20
  end
21
+ Then { @called == command }
25
22
  end
26
23
 
27
- describe "with a couple apps" do
28
- before do
24
+ context "with a couple of apps" do
25
+ Given do
29
26
  @sequence = []
30
27
  @commands = []
31
- @middleware << proc do |command, &block|
28
+ middleware << proc do |command, &block|
32
29
  @commands << command
33
30
  @sequence << "first.before"
34
31
  block.call
35
32
  @sequence << "first.after"
36
33
  end
37
- @middleware << proc do |command, &block|
34
+ middleware << proc do |command, &block|
38
35
  @commands << command
39
36
  @sequence << "second"
40
37
  end
41
- @middleware.call @command
42
- end
43
- it "passes the command to all the apps" do
44
- @commands.should eql [@command, @command]
45
- end
46
- it "calls the first app *around* the second app" do
47
- @sequence.should eql ["first.before", "second", "first.after"]
48
38
  end
39
+ Then { @commands == [command, command] }
40
+ Then { @sequence == ["first.before", "second", "first.after"] }
49
41
 
50
- describe "if the first app does not yield" do
51
- before do
52
- @sequence.clear
53
- @middleware[0] = Proc.new {}
54
- @middleware.call @command
55
- end
56
- it "never calls the second app" do
57
- @sequence.should eql []
58
- end
42
+ context "when the first does not yield to the second" do
43
+ Given { middleware[0] = Proc.new {} }
44
+ Then { @sequence == [] }
59
45
  end
60
46
  end
61
47
 
62
- describe "with an app that yields even though there is no next app" do
63
- before do
64
- app = Object.new
65
- def app.call(command)
66
- yield
67
- end
68
- @middleware << @app
69
- end
70
- it "runs successfully" do
71
- @middleware.call(@command).should eql 0
72
- end
48
+ context "with a single app that yields even though there is no next app" do
49
+ Given { middleware << proc {|c, &block| block.call } }
50
+ Then { @result == 0 }
51
+ end
52
+
53
+ context "when invoked with a 'follow-on' app" do
54
+ When(:result) { middleware.call(command) {|c| c} }
55
+ Then { result == command }
73
56
  end
74
57
  end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+ require "mvcli/path"
3
+
4
+ describe "MVCLI::Path" do
5
+ use_natural_assertions
6
+ Given(:path) { MVCLI::Path.new(Pathname(__FILE__).dirname.join(File.basename __FILE__, '.rb')) }
7
+
8
+ Then { path.exists? 'does/exist' }
9
+ Then { not path.exists? 'does/not/exist' }
10
+ Then { path.read('does/exist') == "Hello World"}
11
+ Then { path.to_s('flim flam') =~ %r{mvcli/spec/mvcli/path_spec/flim flam} }
12
+ Then { path.nearest('spec_helper.rb') == Pathname(__FILE__).parent.parent.join('spec_helper.rb') }
13
+ end
@@ -0,0 +1 @@
1
+ Hello World
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+ require "mvcli/plugins/providers/bundle_provider"
3
+ require "bundler/ui"
4
+
5
+
6
+
7
+ describe "Bundle Provider" do
8
+ # Given(:bundle) { MVCLI::BundleProvider.new }
9
+ # Given(:dir) { Pathname(__FILE__).dirname.join('tmp/plugins') }
10
+ # Given do
11
+ # bundle.stub(:dir) { dir }
12
+ # FileUtils.rm_rf dir.to_s
13
+ # FileUtils.mkdir_p dir.to_s
14
+ # end
15
+ # describe "replacing a gem in the bundle" do
16
+ # When { bundle.replace "trivium-timing", require: 'trivium-timing', path: File.expand_path('../../../../fixtures/plugins/timing-plugin', __FILE__) }
17
+ # When { bundle.replace "ref" }
18
+ # describe "activating the bundle" do
19
+ # When { bundle.activate! }
20
+ # Then { "it's in the classpath now" }
21
+ # end
22
+ # end
23
+ end
@@ -3,54 +3,56 @@ require "mvcli/provisioning"
3
3
 
4
4
  describe "Provisioning" do
5
5
  use_natural_assertions
6
+
6
7
  describe "Scope" do
7
- Given(:provisioner) {{}}
8
- Given(:mod) {Module.new {include MVCLI::Provisioning}}
9
- Given(:cls) {m = mod; Class.new {include m}}
10
- Given(:obj) {cls.new}
11
- Given(:command) {mock(:Command)}
12
- Given(:scope) {MVCLI::Provisioning::Scope.new command, provisioner}
8
+ Given(:scope) { MVCLI::Provisioning::Scope.new options }
9
+ Given(:command) { double :Command }
10
+ Given(:cortex) { double :Cortex }
11
+ Given(:mod) { Module.new {include MVCLI::Provisioning} }
12
+ Given(:cls) { m = mod; Class.new {include m} }
13
+ Given(:obj) { cls.new }
14
+
13
15
  context "when the command is required" do
14
- Given {mod.requires :command}
15
- When(:result) {scope.evaluate {obj.command}}
16
- Then {result == command}
16
+ Given { mod.requires :command }
17
+ Given(:options) { {command: command} }
18
+ When(:result) { scope.evaluate { obj.command } }
19
+ Then { result == command }
20
+ end
21
+ context "when the cortex is required" do
22
+ Given { mod.requires :cortex }
23
+ Given(:options) { {cortex: cortex} }
24
+ Then { scope.evaluate { obj.cortex } == cortex }
17
25
  end
18
26
  context "with a requirement is specified on the module" do
19
- Given {mod.requires :foo}
20
- context "when accessing it but it is not present" do
21
- When(:result) {scope.evaluate {obj.foo}}
22
- Then {result.should have_failed MVCLI::Provisioning::UnsatisfiedRequirement, /foo/}
27
+ Given(:options) { {cortex: cortex} }
28
+ Given { mod.requires :foo }
29
+ context "when reading it but it is not present" do
30
+ Given { cortex.stub(:read) { fail "no such provider" } }
31
+ When(:result) { scope.evaluate { obj.foo } }
32
+ Then { result.should have_failed StandardError, /no such/ }
33
+ And { cortex.should have_received(:read).with(:provider, :foo)}
34
+ context "but it is manually specified in an enclosing block" do
35
+ When(:result) { scope.evaluate(foo: 'bar') { obj.foo} }
36
+ Then { result == 'bar' }
37
+ context "and then not" do
38
+ When(:result) { scope.evaluate { obj.foo } }
39
+ Then { result.should have_failed }
40
+ end
41
+ end
23
42
  end
24
43
  context "and there is a scope which satisfies the requirement" do
25
- Given(:foo) {Object.new}
26
- Given {provisioner[:foo] = foo}
44
+ Given(:foo) { Object.new }
45
+ Given { cortex.stub(:read) { double(:Provider, value: foo)}}
27
46
 
28
- context "when a dependency is accessed in the context of the scope" do
29
- When(:result) {scope.evaluate {obj.foo}}
30
- Then {result == foo}
47
+ context "when a dependency is read in the context of the scope" do
48
+ When(:result) { scope.evaluate { obj.foo } }
49
+ Then { result == foo }
31
50
  end
32
51
  end
33
52
  context "accessing requirements with no scope" do
34
- When(:result) {obj.foo}
35
- Then {result.should have_failed MVCLI::Provisioning::MissingScope}
53
+ When(:result) { obj.foo }
54
+ Then { result.should have_failed MVCLI::Provisioning::MissingScope }
36
55
  end
37
56
  end
38
57
  end
39
-
40
- describe "Provisioner" do
41
- Given do
42
- ActiveSupport::Dependencies.clear
43
- ActiveSupport::Dependencies.autoload_paths.clear
44
- ActiveSupport::Dependencies.autoload_paths << File.expand_path('../dummy/app/providers', __FILE__)
45
- end
46
- Given(:provisioner) {MVCLI::Provisioning::Provisioner.new}
47
- context "when no provider exists for a value" do
48
- When(:result) {provisioner[:does_not_exist]}
49
- Then {result.should have_failed}
50
- end
51
- context "when a provider exists" do
52
- When(:result) {provisioner[:test]}
53
- Then {result == "here is a free value just for you!!"}
54
- end
55
- end
56
58
  end
@@ -4,57 +4,51 @@ require "mvcli/router"
4
4
  describe "MVCLI::Router" do
5
5
  use_natural_assertions
6
6
 
7
- Given(:Router) {MVCLI::Router}
8
- Given(:actions) {mock(:Actions)}
9
- Given(:router) {self.Router.new actions}
10
- Given do
11
- actions.stub(:[]) do |action|
12
- @action = action
13
- ->(command, bindings) {@command = command; @bindings = bindings}
14
- end
15
- end
7
+ Given(:Router) { MVCLI::Router}
8
+ Given(:routes) { self.Router::DSL.new }
9
+ Given(:router) { routes.router }
10
+ Given(:actions) { double(:Actions) }
11
+ Given { actions.stub(:new) { |match, mapping| Map match: match, mapping: mapping } }
12
+ Given { router.stub(:actions) { actions } }
16
13
 
17
14
  def invoke(route = '')
18
- router.call mock(:Command, :argv => route.split(/\s+/))
15
+ router.call double(:Command, :argv => route.split(/\s+/))
19
16
  end
20
17
 
21
18
  context "without any routes" do
22
- When(:result) {invoke}
23
- Then {result.should have_failed self.Router::RoutingError}
19
+ When(:result) { invoke }
20
+ Then { result.should have_failed self.Router::RoutingError }
24
21
  end
25
22
 
26
23
  context "with a route matched to an action" do
27
- Given {router.match 'login' => 'logins#create'}
28
- When {invoke 'login'}
29
- Then { @action == 'logins#create' }
30
- And { not @command.nil? }
31
- Then { @command.argv == ['login'] }
24
+ Given { routes.match 'login' => 'logins#create' }
25
+ When(:action) { invoke 'login' }
26
+ Then { action.mapping == 'logins#create' }
32
27
  end
33
28
 
34
29
  context "when there are command line options, it does not interfere" do
35
- Given { router.match 'login' => 'logins#create' }
36
- When { invoke 'login --then --go-away -f 6 -p' }
37
- Then { not @command.nil? }
30
+ Given { routes.match 'login' => 'logins#create' }
31
+ When(:action) { invoke 'login --then --go-away -f 6 -p' }
32
+ Then { action.mapping == 'logins#create' }
38
33
  end
39
34
 
40
35
  context "with a route matched to a block" do
41
- Given { router.match bam: ->(command) {@command = command} }
42
- When { invoke 'bam' }
43
- Then { @command.argv == ['bam'] }
36
+ Given { routes.match bam: ->(command) { command } }
37
+ When(:action) { invoke 'bam' }
38
+ Then { action.mapping.call('foo') == 'foo' }
44
39
  end
45
40
 
46
41
  context "with a route with captures" do
47
- Given { router.match 'show loadbalancer :id' => 'loadbalancers#show' }
48
- When { invoke 'show loadbalancer 6' }
49
- Then {@action == 'loadbalancers#show'}
50
- And { @command.argv == ['show', 'loadbalancer', '6'] }
51
- And { @bindings[:id] == '6' }
42
+ Given { routes.match 'show loadbalancer :id' => 'loadbalancers#show' }
43
+ When(:action) { invoke 'show loadbalancer 6' }
44
+ Then { action.mapping == 'loadbalancers#show'}
45
+ Then { action.match.bindings[:id] == '6' }
52
46
  end
53
47
 
54
48
  context "with macros" do
55
- Given { router.macro /(-h|--help) (.*)/ => "help \\2" }
56
- Given { router.match "help me" => "help#me"}
57
- When { invoke "--help me" }
58
- Then { @action == 'help#me' }
49
+ Given { routes.macro /(-h|--help) (.*)/ => "help \\2" }
50
+ Given { routes.match "help me" => "help#me"}
51
+ When(:action) { invoke "--help me" }
52
+ Then { action.mapping == 'help#me' }
59
53
  end
60
54
  end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+ require "mvcli/std/extensions/erb_extension"
3
+
4
+ describe "The ERB Extension" do
5
+ Given(:extension) { MVCLI::ERBExtension.new }
6
+ Given(:name) { 'foo/bar' }
7
+
8
+ describe "to_path" do
9
+ When(:path) { extension.to_path name, extension_type }
10
+ context "when the extension_type is a string" do
11
+ Given(:extension_type) { "template" }
12
+ Then { path == "templates/foo/bar.txt.erb" }
13
+ end
14
+ context "when the extension_type is a symbol" do
15
+ Given(:extension_type) { :template }
16
+ Then { path == "templates/foo/bar.txt.erb" }
17
+ end
18
+ context "when the extension_type is not 'template'" do
19
+ Given(:extension_type) { :not_a_template }
20
+ Then { path.should have_failed ArgumentError }
21
+ end
22
+ end
23
+ describe "define" do
24
+ Given(:extension_type) { :template }
25
+ Given(:namespace) { Object }
26
+ When(:template) { extension.define(name, bytes, extension_type, namespace) }
27
+ context "with a valid ERB template" do
28
+ Given(:bytes) { "Hello <%= this.name %>" }
29
+ Given(:output) { StringIO.new }
30
+ When { template.call Map(name: 'Bob'), output }
31
+ Then { output.string == 'Hello Bob' }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ require "spec_helper"
2
+ require "mvcli/std/providers/middleware_provider"
3
+
4
+ describe "building middleware" do
5
+ use_natural_assertions
6
+
7
+ When(:middleware) { MVCLI::MiddlewareProvider.new.value }
8
+ Then { middleware.should_not have_failed }
9
+ And { middleware.length == 2}
10
+ end
@@ -3,27 +3,27 @@ require "mvcli/validatable"
3
3
 
4
4
  describe "a validator" do
5
5
  use_natural_assertions
6
- Given(:object) {Object.new}
7
- Given(:validator) {MVCLI::Validatable::Validator.new}
6
+ Given(:object) { Object.new }
7
+ Given(:validator) { MVCLI::Validatable::Validator.new }
8
8
  Given(:validation) { validator.validate object }
9
9
  Given(:violations) { validation.violations }
10
10
 
11
11
  context "when it validates a field that does not exist on the object" do
12
- Given {validator.validates(:does_not_exist, "invalid") {}}
13
- When(:validation) {validator.validate object}
14
- Then {not validation.errors[:does_not_exist].empty?}
15
- Then {not validation.valid?}
12
+ Given { validator.validates(:does_not_exist, "invalid") {} }
13
+ When(:validation) { validator.validate object }
14
+ Then { not validation.errors[:does_not_exist].empty? }
15
+ Then { not validation.valid? }
16
16
  end
17
17
  describe "validating a child" do
18
- Given {validator.validates_child(:some_child)}
18
+ Given { validator.validates_child(:some_child) }
19
19
  context "when it is nil" do
20
- When(:validation) {validator.validate(mock(:Object, :some_child => nil))}
21
- Then {validation.valid?}
20
+ When(:validation) { validator.validate(double(:Object, :some_child => nil)) }
21
+ Then { validation.valid? }
22
22
  end
23
23
  context "when it does not exist" do
24
- When(:validation) {validator.validate(Object.new)}
25
- Then {not validation.errors[:some_child].nil?}
26
- And {not validation.valid?}
24
+ When(:validation) { validator.validate(Object.new) }
25
+ Then { not validation.errors[:some_child].nil? }
26
+ And { not validation.valid? }
27
27
  end
28
28
  end
29
29