command_model 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +1 -1
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/Gemfile.lock +50 -0
- data/README.md +135 -54
- data/Rakefile +4 -7
- data/bin/rake +27 -0
- data/command_model.gemspec +4 -5
- data/examples/bank/Gemfile +4 -4
- data/examples/bank/app/controllers/accounts_controller.rb +12 -12
- data/examples/bank/app/controllers/transfers_controller.rb +3 -3
- data/examples/bank/app/views/accounts/deposit_form.html.erb +1 -5
- data/examples/bank/app/views/accounts/withdraw_form.html.erb +1 -5
- data/examples/bank/app/views/layouts/application.html.erb +1 -1
- data/examples/bank/app/views/transfers/new.html.erb +2 -6
- data/examples/bank/config/application.rb +1 -1
- data/examples/bank/config/routes.rb +6 -6
- data/examples/bank/test/performance/browsing_test.rb +2 -2
- data/gemfiles/{5.0.gemfile → 6.1.gemfile} +1 -1
- data/gemfiles/6.1.gemfile.lock +52 -0
- data/gemfiles/{5.1.gemfile → 7.0.gemfile} +1 -1
- data/gemfiles/7.0.gemfile.lock +50 -0
- data/gemfiles/7.1.gemfile +5 -0
- data/gemfiles/7.1.gemfile.lock +60 -0
- data/lib/command_model/convert.rb +1 -1
- data/lib/command_model/model.rb +61 -16
- data/lib/command_model/version.rb +1 -1
- data/spec/convert_spec.rb +5 -4
- data/spec/model_spec.rb +129 -6
- metadata +24 -17
- data/.travis.yml +0 -6
@@ -0,0 +1,52 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
command_model (2.0.1)
|
5
|
+
activemodel (> 6.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (6.1.7.7)
|
11
|
+
activesupport (= 6.1.7.7)
|
12
|
+
activesupport (6.1.7.7)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 1.6, < 2)
|
15
|
+
minitest (>= 5.1)
|
16
|
+
tzinfo (~> 2.0)
|
17
|
+
zeitwerk (~> 2.3)
|
18
|
+
concurrent-ruby (1.2.3)
|
19
|
+
diff-lcs (1.5.1)
|
20
|
+
i18n (1.14.4)
|
21
|
+
concurrent-ruby (~> 1.0)
|
22
|
+
minitest (5.22.3)
|
23
|
+
rake (13.1.0)
|
24
|
+
rspec (3.13.0)
|
25
|
+
rspec-core (~> 3.13.0)
|
26
|
+
rspec-expectations (~> 3.13.0)
|
27
|
+
rspec-mocks (~> 3.13.0)
|
28
|
+
rspec-core (3.13.0)
|
29
|
+
rspec-support (~> 3.13.0)
|
30
|
+
rspec-expectations (3.13.0)
|
31
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
32
|
+
rspec-support (~> 3.13.0)
|
33
|
+
rspec-mocks (3.13.0)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.13.0)
|
36
|
+
rspec-support (3.13.1)
|
37
|
+
tzinfo (2.0.6)
|
38
|
+
concurrent-ruby (~> 1.0)
|
39
|
+
zeitwerk (2.6.13)
|
40
|
+
|
41
|
+
PLATFORMS
|
42
|
+
arm64-darwin-23
|
43
|
+
x86_64-linux
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
activemodel (~> 6.1.0)
|
47
|
+
command_model!
|
48
|
+
rake (~> 13.1.0)
|
49
|
+
rspec (~> 3.13.0)
|
50
|
+
|
51
|
+
BUNDLED WITH
|
52
|
+
2.4.3
|
@@ -0,0 +1,50 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
command_model (2.0.1)
|
5
|
+
activemodel (> 6.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (7.0.4.3)
|
11
|
+
activesupport (= 7.0.4.3)
|
12
|
+
activesupport (7.0.4.3)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 1.6, < 2)
|
15
|
+
minitest (>= 5.1)
|
16
|
+
tzinfo (~> 2.0)
|
17
|
+
concurrent-ruby (1.2.3)
|
18
|
+
diff-lcs (1.5.1)
|
19
|
+
i18n (1.14.4)
|
20
|
+
concurrent-ruby (~> 1.0)
|
21
|
+
minitest (5.22.3)
|
22
|
+
rake (13.1.0)
|
23
|
+
rspec (3.13.0)
|
24
|
+
rspec-core (~> 3.13.0)
|
25
|
+
rspec-expectations (~> 3.13.0)
|
26
|
+
rspec-mocks (~> 3.13.0)
|
27
|
+
rspec-core (3.13.0)
|
28
|
+
rspec-support (~> 3.13.0)
|
29
|
+
rspec-expectations (3.13.0)
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
+
rspec-support (~> 3.13.0)
|
32
|
+
rspec-mocks (3.13.0)
|
33
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
34
|
+
rspec-support (~> 3.13.0)
|
35
|
+
rspec-support (3.13.1)
|
36
|
+
tzinfo (2.0.6)
|
37
|
+
concurrent-ruby (~> 1.0)
|
38
|
+
|
39
|
+
PLATFORMS
|
40
|
+
arm64-darwin-23
|
41
|
+
x86_64-linux
|
42
|
+
|
43
|
+
DEPENDENCIES
|
44
|
+
activemodel (~> 7.0.0)
|
45
|
+
command_model!
|
46
|
+
rake (~> 13.1.0)
|
47
|
+
rspec (~> 3.13.0)
|
48
|
+
|
49
|
+
BUNDLED WITH
|
50
|
+
2.4.3
|
@@ -0,0 +1,60 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
command_model (2.0.1)
|
5
|
+
activemodel (> 6.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (7.1.3.2)
|
11
|
+
activesupport (= 7.1.3.2)
|
12
|
+
activesupport (7.1.3.2)
|
13
|
+
base64
|
14
|
+
bigdecimal
|
15
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
|
+
connection_pool (>= 2.2.5)
|
17
|
+
drb
|
18
|
+
i18n (>= 1.6, < 2)
|
19
|
+
minitest (>= 5.1)
|
20
|
+
mutex_m
|
21
|
+
tzinfo (~> 2.0)
|
22
|
+
base64 (0.2.0)
|
23
|
+
bigdecimal (3.1.7)
|
24
|
+
concurrent-ruby (1.2.3)
|
25
|
+
connection_pool (2.4.1)
|
26
|
+
diff-lcs (1.5.1)
|
27
|
+
drb (2.2.1)
|
28
|
+
i18n (1.14.4)
|
29
|
+
concurrent-ruby (~> 1.0)
|
30
|
+
minitest (5.22.3)
|
31
|
+
mutex_m (0.2.0)
|
32
|
+
rake (13.1.0)
|
33
|
+
rspec (3.13.0)
|
34
|
+
rspec-core (~> 3.13.0)
|
35
|
+
rspec-expectations (~> 3.13.0)
|
36
|
+
rspec-mocks (~> 3.13.0)
|
37
|
+
rspec-core (3.13.0)
|
38
|
+
rspec-support (~> 3.13.0)
|
39
|
+
rspec-expectations (3.13.0)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.13.0)
|
42
|
+
rspec-mocks (3.13.0)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.13.0)
|
45
|
+
rspec-support (3.13.1)
|
46
|
+
tzinfo (2.0.6)
|
47
|
+
concurrent-ruby (~> 1.0)
|
48
|
+
|
49
|
+
PLATFORMS
|
50
|
+
arm64-darwin-23
|
51
|
+
x86_64-linux
|
52
|
+
|
53
|
+
DEPENDENCIES
|
54
|
+
activemodel (~> 7.1.0)
|
55
|
+
command_model!
|
56
|
+
rake (~> 13.1.0)
|
57
|
+
rspec (~> 3.13.0)
|
58
|
+
|
59
|
+
BUNDLED WITH
|
60
|
+
2.4.3
|
@@ -61,7 +61,7 @@ module CommandModel
|
|
61
61
|
return nil if value.blank?
|
62
62
|
return value if value.kind_of? Date
|
63
63
|
value = value.to_s
|
64
|
-
if value =~ /\A(\d
|
64
|
+
if value =~ /\A(\d{4,5})-(\d\d)-(\d\d)\z/
|
65
65
|
::Date.civil($1.to_i, $2.to_i, $3.to_i)
|
66
66
|
else
|
67
67
|
::Date.strptime(value, "%m/%d/%Y")
|
data/lib/command_model/model.rb
CHANGED
@@ -1,18 +1,15 @@
|
|
1
1
|
module CommandModel
|
2
|
-
class TypecastError < StandardError
|
3
|
-
attr_reader :original_error
|
4
|
-
|
5
|
-
def initialize(original_error)
|
6
|
-
@original_error = original_error
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
2
|
class Model
|
11
3
|
include ActiveModel::Validations
|
12
4
|
include ActiveModel::Conversion
|
13
5
|
extend ActiveModel::Naming
|
14
6
|
|
15
|
-
|
7
|
+
def self.inherited(subclass)
|
8
|
+
subclass.instance_variable_set :@parameters, parameters.dup.freeze
|
9
|
+
subclass.instance_variable_set :@dependencies, dependencies.dup.freeze
|
10
|
+
end
|
11
|
+
|
12
|
+
Parameter = Data.define(:name, :converters, :validations)
|
16
13
|
|
17
14
|
# Parameter requires one or more attributes as its first parameter(s).
|
18
15
|
# It accepts an options hash as its last parameter.
|
@@ -34,11 +31,12 @@ module CommandModel
|
|
34
31
|
# parameter :height, :weight,
|
35
32
|
# convert: [CommandModel::Convert::StringMutator.new { |s| s.gsub(",", "")}, :integer],
|
36
33
|
# presence: true,
|
37
|
-
# numericality: { :
|
34
|
+
# numericality: { greater_than_or_equal_to: 0 }
|
38
35
|
def self.parameter(*args)
|
39
36
|
options = args.last.kind_of?(Hash) ? args.pop.clone : {}
|
40
37
|
converters = options.delete(:convert)
|
41
38
|
|
39
|
+
@parameters ||= [].freeze
|
42
40
|
args.each do |name|
|
43
41
|
attr_reader name
|
44
42
|
|
@@ -48,13 +46,13 @@ module CommandModel
|
|
48
46
|
attr_writer name
|
49
47
|
end
|
50
48
|
validates name, options.clone if options.present? # clone options because validates mutates the hash :(
|
51
|
-
parameters
|
49
|
+
@parameters = (@parameters + [Parameter.new(name, converters, options)]).freeze
|
52
50
|
end
|
53
51
|
end
|
54
52
|
|
55
53
|
# Returns array of all parameters defined for class
|
56
54
|
def self.parameters
|
57
|
-
@parameters ||= []
|
55
|
+
@parameters ||= [].freeze
|
58
56
|
end
|
59
57
|
|
60
58
|
def self.attr_type_converting_writer(name, converters) #:nodoc
|
@@ -90,6 +88,38 @@ module CommandModel
|
|
90
88
|
end
|
91
89
|
end
|
92
90
|
|
91
|
+
Dependency = Data.define(:name, :default, :allow_blank)
|
92
|
+
|
93
|
+
# Dependency requires one or more attributes as its first parameter(s). A dependency is something that is required
|
94
|
+
# for the command to execute that is not user supplied input. For example, a database connection, a logger, or the
|
95
|
+
# current user.
|
96
|
+
#
|
97
|
+
# ==== Keyword Arguments
|
98
|
+
#
|
99
|
+
# * default - An object that will be used as the default value for the dependency or a callable object that will be
|
100
|
+
# called to get the default value.
|
101
|
+
# * allow_blank - If true, the dependency can be nil or blank. If false, the dependency must be present.
|
102
|
+
#
|
103
|
+
# ==== Examples
|
104
|
+
#
|
105
|
+
# dependency :current_user
|
106
|
+
# dependency :stdout, default: -> { $stdout }
|
107
|
+
def self.dependency(*names, default: nil, allow_blank: false)
|
108
|
+
@dependencies ||= [].freeze
|
109
|
+
names.each do |name|
|
110
|
+
name = name.to_sym
|
111
|
+
attr_reader name
|
112
|
+
private attr_writer name
|
113
|
+
default_callable = default.respond_to?(:call) ? default : -> { default }
|
114
|
+
@dependencies = (@dependencies + [Dependency.new(name, default_callable, allow_blank)]).freeze
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns array of all dependencies defined for class.
|
119
|
+
def self.dependencies
|
120
|
+
@dependencies ||= [].freeze
|
121
|
+
end
|
122
|
+
|
93
123
|
# Executes a block of code if the command model is valid.
|
94
124
|
#
|
95
125
|
# Accepts either a command model or a hash of attributes with which to
|
@@ -97,18 +127,19 @@ module CommandModel
|
|
97
127
|
#
|
98
128
|
# ==== Examples
|
99
129
|
#
|
100
|
-
# RenameUserCommand.execute(:
|
130
|
+
# RenameUserCommand.execute(login: "john") do |command|
|
101
131
|
# if allowed_to_rename_user?
|
102
132
|
# self.login = command.login
|
103
133
|
# else
|
104
134
|
# command.errors.add :base, "not allowed to rename"
|
105
135
|
# end
|
106
136
|
# end
|
107
|
-
def self.execute(attributes_or_command, &block)
|
137
|
+
def self.execute(attributes_or_command, dependencies={}, &block)
|
108
138
|
command = if attributes_or_command.kind_of? self
|
139
|
+
raise ArgumentError, "cannot pass dependencies with already initialized command" if dependencies.present?
|
109
140
|
attributes_or_command
|
110
141
|
else
|
111
|
-
new(attributes_or_command)
|
142
|
+
new(attributes_or_command, dependencies)
|
112
143
|
end
|
113
144
|
|
114
145
|
command.call &block
|
@@ -137,9 +168,23 @@ module CommandModel
|
|
137
168
|
# Accepts a parameters hash or another of the same class. If another
|
138
169
|
# instance of the same class is passed in then the parameters are copied
|
139
170
|
# to the new object.
|
140
|
-
def initialize(parameters={})
|
171
|
+
def initialize(parameters={}, dependencies={})
|
141
172
|
@type_conversion_errors = {}
|
142
173
|
set_parameters parameters
|
174
|
+
|
175
|
+
dependencies = dependencies.symbolize_keys
|
176
|
+
self.class.dependencies.each do |dependency|
|
177
|
+
value = dependencies.fetch(dependency.name, dependency.default.call)
|
178
|
+
if value.blank? && !dependency.allow_blank
|
179
|
+
raise ArgumentError, "Dependency #{dependency.name} cannot be blank"
|
180
|
+
end
|
181
|
+
self.send "#{dependency.name}=", dependencies.fetch(dependency.name, dependency.default.call)
|
182
|
+
end
|
183
|
+
|
184
|
+
unknown_dependencies = dependencies.keys - self.class.dependencies.map(&:name)
|
185
|
+
if unknown_dependencies.present?
|
186
|
+
raise ArgumentError, "Unknown dependencies: #{bad_dependencies.join(", ")}"
|
187
|
+
end
|
143
188
|
end
|
144
189
|
|
145
190
|
# Executes the command by calling the method +execute+ if the validations
|
data/spec/convert_spec.rb
CHANGED
@@ -30,7 +30,7 @@ describe "CommandModel::Convert" do
|
|
30
30
|
expect(subject.("")).to eq(nil)
|
31
31
|
end
|
32
32
|
|
33
|
-
it "raises
|
33
|
+
it "raises ConvertError when invalid string" do
|
34
34
|
expect { subject.("asdf") }.to raise_error(CommandModel::Convert::ConvertError)
|
35
35
|
expect { subject.("0.1") }.to raise_error(CommandModel::Convert::ConvertError)
|
36
36
|
end
|
@@ -52,7 +52,7 @@ describe "CommandModel::Convert" do
|
|
52
52
|
expect(subject.("")).to eq(nil)
|
53
53
|
end
|
54
54
|
|
55
|
-
it "raises
|
55
|
+
it "raises ConvertError when invalid string" do
|
56
56
|
expect { subject.("asdf") }.to raise_error(CommandModel::Convert::ConvertError)
|
57
57
|
end
|
58
58
|
end
|
@@ -81,7 +81,7 @@ describe "CommandModel::Convert" do
|
|
81
81
|
expect(subject.("")).to eq(nil)
|
82
82
|
end
|
83
83
|
|
84
|
-
it "raises
|
84
|
+
it "raises ConvertError when invalid string" do
|
85
85
|
expect { subject.("asdf") }.to raise_error(CommandModel::Convert::ConvertError)
|
86
86
|
end
|
87
87
|
end
|
@@ -93,6 +93,7 @@ describe "CommandModel::Convert" do
|
|
93
93
|
expect(subject.("01/01/2000")).to eq(Date.civil(2000,1,1))
|
94
94
|
expect(subject.("1/1/2000")).to eq(Date.civil(2000,1,1))
|
95
95
|
expect(subject.("2000-01-01")).to eq(Date.civil(2000,1,1))
|
96
|
+
expect(subject.("29000-01-01")).to eq(Date.civil(29000,1,1))
|
96
97
|
end
|
97
98
|
|
98
99
|
it "returns existing date unchanged" do
|
@@ -108,7 +109,7 @@ describe "CommandModel::Convert" do
|
|
108
109
|
expect(subject.("")).to eq(nil)
|
109
110
|
end
|
110
111
|
|
111
|
-
it "raises
|
112
|
+
it "raises ConvertError when invalid string" do
|
112
113
|
expect { subject.("asdf") }.to raise_error(CommandModel::Convert::ConvertError)
|
113
114
|
expect { subject.("3/50/1290") }.to raise_error(CommandModel::Convert::ConvertError)
|
114
115
|
end
|
data/spec/model_spec.rb
CHANGED
@@ -6,7 +6,7 @@ class ExampleCommand < CommandModel::Model
|
|
6
6
|
end
|
7
7
|
|
8
8
|
describe CommandModel::Model do
|
9
|
-
let(:example_command) { ExampleCommand.new :
|
9
|
+
let(:example_command) { ExampleCommand.new name: "John" }
|
10
10
|
let(:invalid_example_command) { ExampleCommand.new }
|
11
11
|
|
12
12
|
describe "self.parameter" do
|
@@ -38,7 +38,7 @@ describe CommandModel::Model do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it "accepts multiple attributes with convert" do
|
41
|
-
klass.parameter :foo, :bar, :
|
41
|
+
klass.parameter :foo, :bar, convert: :integer
|
42
42
|
expect(klass.new.methods).to include(:foo)
|
43
43
|
expect(klass.new.methods).to include(:foo=)
|
44
44
|
expect(klass.new.methods).to include(:bar)
|
@@ -46,7 +46,7 @@ describe CommandModel::Model do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it "accepts multiple attributes with validation" do
|
49
|
-
klass.parameter :foo, :bar, :
|
49
|
+
klass.parameter :foo, :bar, presence: true
|
50
50
|
expect(klass.new.methods).to include(:foo)
|
51
51
|
expect(klass.new.methods).to include(:foo=)
|
52
52
|
expect(klass.new.methods).to include(:bar)
|
@@ -72,6 +72,27 @@ describe CommandModel::Model do
|
|
72
72
|
expect(instance).to_not be_valid
|
73
73
|
expect(instance.errors[:name]).to be_present
|
74
74
|
end
|
75
|
+
|
76
|
+
it "works when model is inherited" do
|
77
|
+
klass.parameter :foo
|
78
|
+
expect(klass.parameters.map(&:name)).to eq([:foo])
|
79
|
+
|
80
|
+
klass_b = Class.new(klass)
|
81
|
+
expect(klass_b.parameters.map(&:name)).to eq([:foo])
|
82
|
+
klass_b.parameter :bar
|
83
|
+
expect(klass.parameters.map(&:name)).to eq([:foo])
|
84
|
+
expect(klass_b.parameters.map(&:name)).to eq([:foo, :bar])
|
85
|
+
|
86
|
+
klass_c = Class.new(klass_b)
|
87
|
+
expect(klass.parameters.map(&:name)).to eq([:foo])
|
88
|
+
expect(klass_b.parameters.map(&:name)).to eq([:foo, :bar])
|
89
|
+
expect(klass_c.parameters.map(&:name)).to eq([:foo, :bar])
|
90
|
+
|
91
|
+
klass_c.parameter :baz
|
92
|
+
expect(klass.parameters.map(&:name)).to eq([:foo])
|
93
|
+
expect(klass_b.parameters.map(&:name)).to eq([:foo, :bar])
|
94
|
+
expect(klass_c.parameters.map(&:name)).to eq([:foo, :bar, :baz])
|
95
|
+
end
|
75
96
|
end
|
76
97
|
|
77
98
|
describe "self.parameters" do
|
@@ -89,13 +110,65 @@ describe CommandModel::Model do
|
|
89
110
|
end
|
90
111
|
end
|
91
112
|
|
113
|
+
describe "self.dependency" do
|
114
|
+
let(:klass) { Class.new(CommandModel::Model) }
|
115
|
+
|
116
|
+
it "creates an attribute reader" do
|
117
|
+
klass.dependency :foo, allow_blank: true
|
118
|
+
expect(klass.new.methods).to include(:foo)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "accepts multiple attributes" do
|
122
|
+
klass.dependency :foo, :bar, allow_blank: true
|
123
|
+
expect(klass.new.methods).to include(:foo)
|
124
|
+
expect(klass.new.methods).to include(:bar)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "accepts multiple attributes with default" do
|
128
|
+
klass.dependency :foo, :bar, default: -> { "baz" }
|
129
|
+
expect(klass.new.methods).to include(:foo)
|
130
|
+
expect(klass.new.methods).to include(:bar)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "works when model is inherited" do
|
134
|
+
klass.dependency :foo
|
135
|
+
expect(klass.dependencies.map(&:name)).to eq([:foo])
|
136
|
+
|
137
|
+
klass_b = Class.new(klass)
|
138
|
+
expect(klass_b.dependencies.map(&:name)).to eq([:foo])
|
139
|
+
klass_b.dependency :bar
|
140
|
+
expect(klass.dependencies.map(&:name)).to eq([:foo])
|
141
|
+
expect(klass_b.dependencies.map(&:name)).to eq([:foo, :bar])
|
142
|
+
|
143
|
+
klass_c = Class.new(klass_b)
|
144
|
+
expect(klass.dependencies.map(&:name)).to eq([:foo])
|
145
|
+
expect(klass_b.dependencies.map(&:name)).to eq([:foo, :bar])
|
146
|
+
expect(klass_c.dependencies.map(&:name)).to eq([:foo, :bar])
|
147
|
+
|
148
|
+
klass_c.dependency :baz
|
149
|
+
expect(klass.dependencies.map(&:name)).to eq([:foo])
|
150
|
+
expect(klass_b.dependencies.map(&:name)).to eq([:foo, :bar])
|
151
|
+
expect(klass_c.dependencies.map(&:name)).to eq([:foo, :bar, :baz])
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "self.dependencies" do
|
156
|
+
it "returns all dependencies in class" do
|
157
|
+
klass = Class.new(CommandModel::Model)
|
158
|
+
klass.dependency :foo
|
159
|
+
klass.dependency :bar
|
160
|
+
|
161
|
+
expect(klass.dependencies.map(&:name)).to eq([:foo, :bar])
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
92
165
|
describe "self.execute" do
|
93
166
|
it "accepts object of same kind and returns it" do
|
94
167
|
expect(ExampleCommand.execute(example_command) {}).to eq(example_command)
|
95
168
|
end
|
96
169
|
|
97
170
|
it "accepts attributes, creates object, and returns it" do
|
98
|
-
c = ExampleCommand.execute(:
|
171
|
+
c = ExampleCommand.execute(name: "John") {}
|
99
172
|
expect(c).to be_kind_of(ExampleCommand)
|
100
173
|
expect(c.name).to eq("John")
|
101
174
|
end
|
@@ -129,6 +202,25 @@ describe CommandModel::Model do
|
|
129
202
|
|
130
203
|
expect(example_command).to_not be_success
|
131
204
|
end
|
205
|
+
|
206
|
+
it "uses default dependencies when not provided" do
|
207
|
+
klass = Class.new(CommandModel::Model)
|
208
|
+
klass.dependency :stdout, default: -> { $stdout }
|
209
|
+
klass.parameter :name
|
210
|
+
m = klass.execute(name: "John")
|
211
|
+
expect(m.stdout).to eq($stdout)
|
212
|
+
expect(m.execution_attempted?).to eq(true)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "accepts dependencies from arguments" do
|
216
|
+
klass = Class.new(CommandModel::Model)
|
217
|
+
klass.dependency :stdout, default: -> { $stdout }
|
218
|
+
klass.parameter :name
|
219
|
+
writer = StringIO.new
|
220
|
+
m = klass.execute({name: "John"}, stdout: writer)
|
221
|
+
expect(m.stdout).to eq(writer)
|
222
|
+
expect(m.execution_attempted?).to eq(true)
|
223
|
+
end
|
132
224
|
end
|
133
225
|
|
134
226
|
describe "self.success" do
|
@@ -151,15 +243,46 @@ describe CommandModel::Model do
|
|
151
243
|
|
152
244
|
describe "initialize" do
|
153
245
|
it "assigns parameters from hash" do
|
154
|
-
m = ExampleCommand.new :
|
246
|
+
m = ExampleCommand.new name: "John"
|
155
247
|
expect(m.name).to eq("John")
|
156
248
|
end
|
157
249
|
|
158
250
|
it "assigns parameters from other CommandModel" do
|
159
|
-
other = ExampleCommand.new :
|
251
|
+
other = ExampleCommand.new name: "John"
|
160
252
|
m = ExampleCommand.new other
|
161
253
|
expect(m.name).to eq(other.name)
|
162
254
|
end
|
255
|
+
|
256
|
+
it "assigns default dependencies when not provided" do
|
257
|
+
klass = Class.new(CommandModel::Model)
|
258
|
+
klass.dependency :stdout, default: -> { $stdout }
|
259
|
+
klass.parameter :name
|
260
|
+
m = klass.new name: "John"
|
261
|
+
expect(m.stdout).to eq($stdout)
|
262
|
+
end
|
263
|
+
|
264
|
+
it "assigns dependencies from arguments" do
|
265
|
+
klass = Class.new(CommandModel::Model)
|
266
|
+
klass.dependency :stdout, default: -> { $stdout }
|
267
|
+
klass.parameter :name
|
268
|
+
writer = StringIO.new
|
269
|
+
m = klass.new({name: "John"}, stdout: writer)
|
270
|
+
expect(m.stdout).to eq(writer)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "raises error when dependency is missing" do
|
274
|
+
klass = Class.new(CommandModel::Model)
|
275
|
+
klass.dependency :stdout
|
276
|
+
klass.parameter :name
|
277
|
+
expect { klass.new name: "John" }.to raise_error(StandardError)
|
278
|
+
end
|
279
|
+
|
280
|
+
it "does not raise error when allow blank dependency is missing" do
|
281
|
+
klass = Class.new(CommandModel::Model)
|
282
|
+
klass.dependency :stdout, allow_blank: true
|
283
|
+
klass.parameter :name
|
284
|
+
expect { klass.new name: "John" }.to_not raise_error
|
285
|
+
end
|
163
286
|
end
|
164
287
|
|
165
288
|
describe "call" do
|