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.
- checksums.yaml +4 -4
- data/Changelog.md +4 -0
- data/Gemfile +2 -2
- data/README.md +6 -2
- data/app.rb +7 -0
- data/app/routes.rb +0 -0
- data/bin/mvcli +5 -0
- data/lib/mvcli.rb +1 -0
- data/lib/mvcli/action.rb +31 -0
- data/lib/mvcli/app.rb +23 -28
- data/lib/mvcli/controller.rb +20 -2
- data/lib/mvcli/core.rb +101 -0
- data/lib/mvcli/cortex.rb +38 -0
- data/lib/mvcli/form.rb +13 -0
- data/lib/mvcli/loader.rb +45 -1
- data/lib/mvcli/middleware.rb +13 -15
- data/lib/mvcli/path.rb +41 -0
- data/lib/mvcli/plugins.rb +19 -0
- data/lib/mvcli/plugins/controllers/plugins_controller.rb +23 -0
- data/lib/mvcli/plugins/forms/plugins/install_form.rb +6 -0
- data/lib/mvcli/plugins/models/plugins/installation_model.rb +33 -0
- data/lib/mvcli/plugins/providers/bundle_provider.rb +116 -0
- data/lib/mvcli/plugins/routes.rb +3 -0
- data/lib/mvcli/plugins/templates/plugins/index.txt.erb +3 -0
- data/lib/mvcli/plugins/templates/plugins/install.txt.erb +1 -0
- data/lib/mvcli/plugins/templates/plugins/uninstall.txt.erb +1 -0
- data/lib/mvcli/provisioning.rb +48 -38
- data/lib/mvcli/router.rb +34 -20
- data/lib/mvcli/std/extensions/erb_extension.rb +24 -0
- data/lib/mvcli/std/providers/argv_provider.rb +9 -0
- data/lib/mvcli/std/providers/config_provider.rb +29 -0
- data/lib/mvcli/std/providers/middleware_provider.rb +12 -0
- data/lib/mvcli/std/providers/router_provider.rb +17 -0
- data/lib/mvcli/std/routes.rb +2 -0
- data/lib/mvcli/version.rb +1 -1
- data/spec/features/managing_plugins_spec.rb +29 -0
- data/spec/fixtures/apps/trivium/app.rb +10 -0
- data/spec/fixtures/apps/trivium/app/routes.rb +0 -0
- data/spec/fixtures/apps/trivium/bin/trivium +6 -0
- data/spec/fixtures/apps/trivium/lib/trivium/version.rb +3 -0
- data/spec/fixtures/apps/trivium/trivium.gemspec +8 -0
- data/spec/fixtures/bin/trivium +3 -0
- data/spec/fixtures/plugins/timing-plugin/app/routes.rb +1 -0
- data/spec/fixtures/plugins/timing-plugin/lib/trivium-timing.rb +5 -0
- data/spec/fixtures/plugins/timing-plugin/trivium-timing.gemspec +9 -0
- data/spec/mvcli/action_spec.rb +31 -0
- data/spec/mvcli/controller_spec.rb +35 -0
- data/spec/mvcli/core_spec.rb +77 -0
- data/spec/mvcli/cortex_spec.rb +50 -0
- data/spec/mvcli/erb_spec.rb +1 -1
- data/spec/mvcli/form_spec.rb +1 -1
- data/spec/mvcli/loader_spec.rb +58 -13
- data/spec/mvcli/middleware/exception_logger_spec.rb +3 -3
- data/spec/mvcli/middleware/exit_status_spec.rb +10 -10
- data/spec/mvcli/middleware_spec.rb +28 -45
- data/spec/mvcli/path_spec.rb +13 -0
- data/spec/mvcli/path_spec/does/exist +1 -0
- data/spec/mvcli/plugins/providers/bundle_provider_spec.rb +23 -0
- data/spec/mvcli/provisioning_spec.rb +39 -37
- data/spec/mvcli/router_spec.rb +26 -32
- data/spec/mvcli/std/extensions/erb_extension_spec.rb +34 -0
- data/spec/mvcli/std/providers/middleware_provider_spec.rb +10 -0
- data/spec/mvcli/validatable_spec.rb +12 -12
- data/spec/spec_helper.rb +13 -1
- data/spec/support/aruba_helper.rb +76 -0
- metadata +69 -33
- data/lib/mvcli/actions.rb +0 -37
- data/lib/mvcli/renderer.rb +0 -16
- data/spec/mvcli/actions_spec.rb +0 -34
- data/spec/mvcli/dummy/app/providers/test_provider.rb +0 -5
data/lib/mvcli/path.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module MVCLI
|
4
|
+
class Path
|
5
|
+
def initialize(base)
|
6
|
+
@base = Pathname(base.to_s)
|
7
|
+
end
|
8
|
+
|
9
|
+
def exists?(path)
|
10
|
+
@base.join(path).exist?
|
11
|
+
end
|
12
|
+
|
13
|
+
def read(path)
|
14
|
+
@base.join(path).read
|
15
|
+
end
|
16
|
+
|
17
|
+
def join(path)
|
18
|
+
self.class.new @base.join path
|
19
|
+
end
|
20
|
+
|
21
|
+
def nearest(pattern)
|
22
|
+
ancestors.each do |dir|
|
23
|
+
if entry = dir.entries.find { |e| e.to_s.match pattern }
|
24
|
+
return dir.join entry
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ancestors(dir = @base)
|
30
|
+
if dir == dir.parent
|
31
|
+
[]
|
32
|
+
else
|
33
|
+
[dir] + ancestors(dir.parent)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s(path = nil)
|
38
|
+
path.nil? ? @base.to_s : @base.join(path).to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "mvcli/core"
|
2
|
+
|
3
|
+
module MVCLI
|
4
|
+
class Plugins < MVCLI::Core
|
5
|
+
requires :bundle, :cortex
|
6
|
+
self.path = File.expand_path '../plugins', __FILE__
|
7
|
+
self.namespace = ::MVCLI
|
8
|
+
self.identifier = 'mvcli-plugins'
|
9
|
+
|
10
|
+
def activate!
|
11
|
+
bundle.activate!
|
12
|
+
Core.drain do |cls|
|
13
|
+
core = cls.new if cls.path
|
14
|
+
core.activate!
|
15
|
+
cortex << core
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class MVCLI::PluginsController < MVCLI::Controller
|
2
|
+
requires :cortex, :argv, :bundle
|
3
|
+
|
4
|
+
def install
|
5
|
+
@installation = bundle.replace params[:name], form.attributes
|
6
|
+
respond_with @installation
|
7
|
+
end
|
8
|
+
|
9
|
+
def uninstall
|
10
|
+
@installation = bundle.remove params[:name]
|
11
|
+
respond_with @installation
|
12
|
+
end
|
13
|
+
|
14
|
+
def index
|
15
|
+
@plugins = bundle.plugins
|
16
|
+
respond_with @plugins
|
17
|
+
end
|
18
|
+
|
19
|
+
def respond_with(response, options = {})
|
20
|
+
response
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class MVCLI::Plugins::InstallationModel
|
2
|
+
requires :config
|
3
|
+
|
4
|
+
def initialize(form)
|
5
|
+
@form = form
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
gemspec.name
|
10
|
+
end
|
11
|
+
|
12
|
+
def version
|
13
|
+
gemspec.version
|
14
|
+
end
|
15
|
+
|
16
|
+
def location
|
17
|
+
@form.path
|
18
|
+
end
|
19
|
+
|
20
|
+
def gemspec
|
21
|
+
gemspec = Gem::Specification.load Dir[@form.path.join('*.gemspec')].first
|
22
|
+
config.directory "plugins" do |dir|
|
23
|
+
target = dir.join(gemspec.name)
|
24
|
+
FileUtils.rm_rf target
|
25
|
+
target.make_symlink location
|
26
|
+
Gem.paths.path.unshift dir.to_s
|
27
|
+
request = Gem::RequestSet.new *gemspec.dependencies
|
28
|
+
request.resolve
|
29
|
+
request.install_into dir
|
30
|
+
end
|
31
|
+
return gemspec
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
class MVCLI::BundleProvider
|
2
|
+
requires :config
|
3
|
+
|
4
|
+
def value
|
5
|
+
self
|
6
|
+
end
|
7
|
+
|
8
|
+
def activate!
|
9
|
+
require activatefile if activatefile.exist?
|
10
|
+
end
|
11
|
+
|
12
|
+
def replace(name, options = {})
|
13
|
+
require 'bundler'
|
14
|
+
builder.dependencies.reject! { |dep| dep.name == name }
|
15
|
+
dep, *rest = builder.gem name, options
|
16
|
+
update! name
|
17
|
+
write!
|
18
|
+
return dep
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove(gem_name)
|
22
|
+
require 'bundler'
|
23
|
+
dep = builder.dependencies.find { |d| d.name == gem_name }
|
24
|
+
fail "#{gem_name} is not an installed plugin" unless dep
|
25
|
+
builder.dependencies.reject! { |d| d == dep }
|
26
|
+
update! gem_name
|
27
|
+
write!
|
28
|
+
return dep
|
29
|
+
end
|
30
|
+
|
31
|
+
def plugins
|
32
|
+
lock.specs.select do |spec|
|
33
|
+
builder.dependencies.detect { |dep| dep.name == spec.name }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def gemfile
|
38
|
+
dir.join('Gemfile').tap do |path|
|
39
|
+
unless path.exist?
|
40
|
+
path.open "wb" do |file|
|
41
|
+
file.puts "source 'https://rubygems.org'"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def lockfile
|
48
|
+
dir.join 'Gemfile.lock'
|
49
|
+
end
|
50
|
+
|
51
|
+
def setupfile
|
52
|
+
dir.join 'bundle/bundler/setup.rb'
|
53
|
+
end
|
54
|
+
|
55
|
+
def activatefile
|
56
|
+
dir.join 'activate.rb'
|
57
|
+
end
|
58
|
+
|
59
|
+
def dir
|
60
|
+
config.directory('plugins')
|
61
|
+
end
|
62
|
+
|
63
|
+
def builder
|
64
|
+
@builder ||= Bundler::Dsl.new.tap do |builder|
|
65
|
+
builder.eval_gemfile gemfile
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def lock
|
70
|
+
require 'bundler'
|
71
|
+
Bundler::LockfileParser.new lockfile.read
|
72
|
+
end
|
73
|
+
|
74
|
+
def write!
|
75
|
+
gemfile.open "w" do |file|
|
76
|
+
file.puts "source 'https://rubygems.org'"
|
77
|
+
builder.dependencies.each do |dep|
|
78
|
+
file << %|gem "#{dep.name}"|
|
79
|
+
if req = dep.requirements_list.first
|
80
|
+
file << %|, "#{req}"|
|
81
|
+
end
|
82
|
+
options = dep.source ? dep.source.options || {} : {}
|
83
|
+
options = Hash[options.map { |k, v| [k,v.to_s]}]
|
84
|
+
options.merge! "require" => dep.autorequire if dep.autorequire
|
85
|
+
file << %|, #{options.inspect}|
|
86
|
+
file << "\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
activatefile.open "w" do |file|
|
90
|
+
file.puts "require #{setupfile.to_s.inspect}"
|
91
|
+
builder.dependencies.each do |dep|
|
92
|
+
require_names = dep.autorequire || [dep.name]
|
93
|
+
require_names.each do |name|
|
94
|
+
file.puts "require #{name.inspect}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def update!(name)
|
101
|
+
path = Bundler.settings[:path]
|
102
|
+
original_definition_method = Bundler.method(:definition)
|
103
|
+
Bundler.with_clean_env do
|
104
|
+
ENV['BUNDLE_GEMFILE'] = gemfile.to_s
|
105
|
+
definition = builder.to_definition(lockfile, name => true)
|
106
|
+
Bundler.settings[:path] = dir.join('bundle').to_s
|
107
|
+
Dir.chdir dir do
|
108
|
+
Bundler.define_singleton_method(:definition) { definition }
|
109
|
+
Bundler::Installer.install dir, definition, standalone: [], "update" => true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
ensure
|
113
|
+
Bundler.define_singleton_method(:definition, &original_definition_method)
|
114
|
+
Bundler.settings[:path] = path
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Installed plugin <%= this.name %> <%= this.requirement %>
|
@@ -0,0 +1 @@
|
|
1
|
+
Uninstalled plugin <%= this.name %>
|
data/lib/mvcli/provisioning.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
require "map"
|
2
|
-
require "active_support/concern"
|
3
|
-
require "active_support/dependencies"
|
4
|
-
require "mvcli/loader"
|
5
2
|
|
6
3
|
module MVCLI
|
7
4
|
module Provisioning
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
UnsatisfiedRequirement = Class.new StandardError
|
10
5
|
MissingScope = Class.new StandardError
|
11
6
|
|
12
|
-
|
7
|
+
def self.included(base)
|
8
|
+
base.send :extend, Requires
|
9
|
+
end
|
10
|
+
|
11
|
+
module Requires
|
13
12
|
def requires(*deps)
|
14
13
|
deps.each do |dep|
|
15
14
|
self.send(:define_method, dep) {Scope[dep]}
|
@@ -17,61 +16,72 @@ module MVCLI
|
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
19
|
+
|
20
|
+
::Object.send :include, self
|
21
|
+
|
20
22
|
class Scope
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
requires :cortex
|
24
|
+
|
25
|
+
def initialize(options = {}, &block)
|
26
|
+
@providers = Map options
|
27
|
+
evaluate &block if block_given?
|
24
28
|
end
|
25
29
|
|
26
30
|
def [](name)
|
27
|
-
|
31
|
+
unless provider = @providers[name]
|
32
|
+
provider = @providers[name] = cortex.read :provider, name
|
33
|
+
end
|
34
|
+
if provider.respond_to?(:value)
|
35
|
+
provider.value
|
36
|
+
elsif provider.respond_to?(:instance_methods) && provider.instance_methods.member?(:value)
|
37
|
+
provider.new.value
|
38
|
+
else
|
39
|
+
provider
|
40
|
+
end
|
28
41
|
end
|
29
42
|
|
30
|
-
def evaluate
|
43
|
+
def evaluate(names = {})
|
31
44
|
old = self.class.current
|
45
|
+
providers = @providers
|
46
|
+
@providers = Map @providers.to_hash.merge(names)
|
32
47
|
self.class.current = self
|
33
48
|
yield
|
34
49
|
ensure
|
50
|
+
@providers = providers
|
35
51
|
self.class.current = old
|
36
52
|
end
|
37
53
|
|
38
|
-
|
39
|
-
Thread.current[self.class.name]
|
40
|
-
end
|
54
|
+
private
|
41
55
|
|
42
|
-
def
|
43
|
-
|
56
|
+
def const(value)
|
57
|
+
Constant.new value
|
44
58
|
end
|
45
59
|
|
46
|
-
|
47
|
-
|
48
|
-
|
60
|
+
class << self
|
61
|
+
def current
|
62
|
+
Thread.current[self.class.name]
|
63
|
+
end
|
49
64
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
65
|
+
def current!
|
66
|
+
current or fail MissingScope, "attempting to access scope, but none is active!"
|
67
|
+
end
|
54
68
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
unless provider = @providers[name]
|
62
|
-
provider = @providers[name] = @loader.load :provider, name
|
69
|
+
def current=(scope)
|
70
|
+
Thread.current[self.class.name] = scope
|
71
|
+
end
|
72
|
+
|
73
|
+
def [](name)
|
74
|
+
current![name]
|
63
75
|
end
|
64
|
-
provider.value
|
65
76
|
end
|
66
|
-
end
|
67
77
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
78
|
+
class Constant
|
79
|
+
attr_reader :value
|
80
|
+
|
81
|
+
def initialize(value)
|
82
|
+
@value = value
|
72
83
|
end
|
73
84
|
end
|
74
85
|
end
|
75
|
-
::Object.send :include, self
|
76
86
|
end
|
77
87
|
end
|
data/lib/mvcli/router.rb
CHANGED
@@ -5,35 +5,52 @@ require "mvcli/argv"
|
|
5
5
|
module MVCLI
|
6
6
|
class Router
|
7
7
|
class RoutingError < StandardError; end
|
8
|
+
requires :actions
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
attr_reader :routes
|
11
|
+
attr_reader :macros
|
12
|
+
|
13
|
+
def initialize
|
11
14
|
@routes = []
|
12
15
|
@macros = []
|
13
16
|
end
|
14
17
|
|
15
|
-
def macro(options)
|
16
|
-
@macros.push Macro.new options
|
17
|
-
end
|
18
|
-
|
19
|
-
def match(options)
|
20
|
-
pattern, action = options.first
|
21
|
-
options.delete pattern
|
22
|
-
@routes << Route.new(pattern, @actions, action, options)
|
23
|
-
end
|
24
|
-
|
25
18
|
def call(command)
|
26
19
|
argv = @macros.reduce(command.argv) do |args, macro|
|
27
20
|
macro.expand args
|
28
21
|
end
|
29
22
|
@routes.each do |route|
|
30
|
-
if
|
31
|
-
return
|
23
|
+
if action = route.match(argv)
|
24
|
+
return action
|
32
25
|
end
|
33
26
|
end
|
34
27
|
fail RoutingError, "no route matches '#{command.argv.join ' '}'"
|
35
28
|
end
|
36
29
|
|
30
|
+
alias_method :[], :call
|
31
|
+
|
32
|
+
class DSL < BasicObject
|
33
|
+
attr_reader :router
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@router = Router.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def macro(options)
|
40
|
+
@router.macros.push Macro.new options
|
41
|
+
end
|
42
|
+
|
43
|
+
def match(options)
|
44
|
+
pattern, action = options.first
|
45
|
+
options.delete pattern
|
46
|
+
@router.routes << Route.new(router, pattern, action, options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def proc(&body)
|
50
|
+
::Proc.new &body
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
37
54
|
class Macro
|
38
55
|
def initialize(options)
|
39
56
|
@pattern, @expansion = options.first
|
@@ -45,19 +62,16 @@ module MVCLI
|
|
45
62
|
end
|
46
63
|
|
47
64
|
class Route
|
48
|
-
def initialize(
|
65
|
+
def initialize(router, pattern, action, options = {})
|
49
66
|
@pattern = Pattern.new pattern.to_s
|
50
|
-
@
|
67
|
+
@router, @action, @options = router, action, options
|
51
68
|
end
|
52
69
|
|
53
70
|
def match(argv)
|
54
71
|
argv = MVCLI::Argv.new argv
|
55
72
|
match = @pattern.match(argv.arguments)
|
56
73
|
if match.matches?
|
57
|
-
|
58
|
-
action = @actions[@action] or fail "no action found for #{@action}"
|
59
|
-
action.call command, match.bindings
|
60
|
-
end
|
74
|
+
@router.actions.new match, @action
|
61
75
|
end
|
62
76
|
end
|
63
77
|
end
|