mutations 0.5.10 → 0.5.11

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mutations (0.5.9)
4
+ mutations (0.5.11)
5
5
  activesupport
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -114,7 +114,7 @@ That being said, you can pass multiple hashes to run, and they are merged togeth
114
114
  ```ruby
115
115
  # A user comments on an article
116
116
  class CreateComment < Mutations::Command
117
- requried do
117
+ required do
118
118
  model :user
119
119
  model :article
120
120
  string :comment, max_length: 500
@@ -138,7 +138,7 @@ Here, we pass two hashes to CreateComment. Even if the params[:comment] hash has
138
138
  1. Subclass Mutations::Command
139
139
 
140
140
  ```ruby
141
- class YourMutation < Mutatons::Command
141
+ class YourMutation < Mutations::Command
142
142
  # ...
143
143
  end
144
144
  ```
data/TODO CHANGED
@@ -1,2 +1,14 @@
1
1
  - Document default behavior discarding of nils for optional params
2
2
  - document string.discard_empty
3
+
4
+ - protected parameters:
5
+ optional do
6
+ boolean :skip_confirmation, protected: true
7
+ end
8
+ Given the above, skip_confirmation is only accepted as a parameter if it's passed in a later hash, eg this would make it take:
9
+ User::ChangeEmail.run!(params, user: current_user, skip_confirmation: true)
10
+ But this would not:
11
+ params = {user: current_user, skip_confirmation: true}
12
+ User::ChangeEmail.run!(params)
13
+
14
+
@@ -1,25 +1,10 @@
1
- # IDEA i just had (protected parameters):
2
- # optional do
3
- # boolean :skip_confirmation, protected: true
4
- # end
5
- # Given the above, skip_confirmation is only accepted as a parameter if it's passed in a later hash, eg this would make it take:
6
- # User::ChangeEmail.run!(params, user: current_user, skip_confirmation: true)
7
- # But this would not:
8
- # params = {user: current_user, skip_confirmation: true}
9
- # User::ChangeEmail.run!(params)
10
-
11
-
12
1
  module Mutations
13
2
  class Command
14
-
15
- ##
16
- ##
17
- ##
18
3
  class << self
19
- def required(&block)
20
- self.input_filters.required(&block)
21
-
22
- self.input_filters.required_keys.each do |key|
4
+ def create_attr_methods(meth, &block)
5
+ self.input_filters.send(meth, &block)
6
+ keys = self.input_filters.send("#{meth}_keys")
7
+ keys.each do |key|
23
8
  define_method(key) do
24
9
  @filtered_input[key]
25
10
  end
@@ -33,41 +18,27 @@ module Mutations
33
18
  end
34
19
  end
35
20
  end
21
+ private :create_attr_methods
36
22
 
37
- def optional(&block)
38
- self.input_filters.optional(&block)
39
-
40
- self.input_filters.optional_keys.each do |key|
41
- define_method(key) do
42
- @filtered_input[key]
43
- end
44
-
45
- define_method("#{key}_present?") do
46
- @filtered_input.has_key?(key)
47
- end
23
+ def required(&block)
24
+ create_attr_methods(:required, &block)
25
+ end
48
26
 
49
- define_method("#{key}=") do |v|
50
- @filtered_input[key] = v
51
- end
52
- end
27
+ def optional(&block)
28
+ create_attr_methods(:optional, &block)
53
29
  end
54
30
 
55
31
  def run(*args)
56
- new(*args).execute!
32
+ new(*args).run
57
33
  end
58
34
 
59
35
  def run!(*args)
60
- m = run(*args)
61
- if m.success?
62
- m.result
63
- else
64
- raise ValidationException.new(m.errors)
65
- end
36
+ new(*args).run!
66
37
  end
67
38
 
68
39
  # Validates input, but doesn't call execute. Returns an Outcome with errors anyway.
69
40
  def validate(*args)
70
- new(*args).validation_outcome
41
+ new(*args).validate
71
42
  end
72
43
 
73
44
  def input_filters
@@ -84,19 +55,10 @@ module Mutations
84
55
 
85
56
  # Instance methods
86
57
  def initialize(*args)
87
- if args.length == 0
88
- @original_hash = {}
89
- else
90
- @original_hash = args.shift
91
- raise ArgumentError.new("All arguments must be hashes") unless @original_hash.is_a?(Hash)
92
- @original_hash = @original_hash.with_indifferent_access
58
+ @original_hash = args.each_with_object({}.with_indifferent_access) do |arg, h|
59
+ raise ArgumentError.new("All arguments must be hashes") unless arg.is_a?(Hash)
60
+ h.merge!(arg)
93
61
  end
94
-
95
- args.each do |a|
96
- raise ArgumentError.new("All arguments must be hashes") unless a.is_a?(Hash)
97
- @original_hash.merge!(a)
98
- end
99
-
100
62
  @filtered_input, @errors = self.input_filters.filter(@original_hash)
101
63
  end
102
64
 
@@ -104,26 +66,37 @@ module Mutations
104
66
  self.class.input_filters
105
67
  end
106
68
 
107
- def execute!
108
- return Outcome.new(false, nil, @errors) if @errors
69
+ def has_errors?
70
+ !@errors.nil?
71
+ end
109
72
 
110
- # IDEA/TODO: run validate block
73
+ def run
74
+ return validation_outcome if has_errors?
75
+ validation_outcome(execute)
76
+ end
111
77
 
112
- r = execute
113
- if @errors # Execute can add errors
114
- return Outcome.new(false, nil, @errors)
78
+ def run!
79
+ outcome = run
80
+ if outcome.success?
81
+ outcome.result
115
82
  else
116
- return Outcome.new(true, r, nil)
83
+ raise ValidationException.new(outcome.errors)
117
84
  end
118
85
  end
119
86
 
87
+ def validate
88
+ validation_outcome
89
+ end
90
+
120
91
  # Runs input thru the filter and sets @filtered_input and @errors
121
- def validation_outcome
122
- if @errors
123
- Outcome.new(false, nil, @errors)
124
- else
125
- Outcome.new(true, nil, nil)
126
- end
92
+ def validation_outcome(result = nil)
93
+ Outcome.new(!has_errors?, has_errors? ? nil : result, @errors, @filtered_input)
94
+ end
95
+
96
+ protected
97
+
98
+ def execute
99
+ # Meant to be overridden
127
100
  end
128
101
 
129
102
  # add_error("name", :too_short)
@@ -134,18 +107,13 @@ module Mutations
134
107
  raise ArgumentError.new("Invalid kind") unless kind.is_a?(Symbol)
135
108
 
136
109
  @errors ||= ErrorHash.new
137
- cur_errors = @errors
138
- parts = key.to_s.split(".")
139
- while part = parts.shift
140
- part = part.to_sym
141
- if parts.length > 0
142
- cur_errors[part] = ErrorHash.new unless cur_errors[part].is_a?(ErrorHash)
143
- cur_errors = cur_errors[part]
144
- else
145
- cur_errors[part] = ErrorAtom.new(key, kind, message: message)
110
+ @errors.tap do |errs|
111
+ *path, last = key.to_s.split(".")
112
+ inner = path.inject(errs) do |cut_errors,part|
113
+ cur_errors[part.to_sym] ||= ErrorHash.new
146
114
  end
115
+ inner[last] = ErrorAtom.new(key, kind, message: message)
147
116
  end
148
- @errors
149
117
  end
150
118
 
151
119
  def merge_errors(hash)
@@ -156,9 +124,5 @@ module Mutations
156
124
  def inputs
157
125
  @filtered_input
158
126
  end
159
-
160
- def execute
161
- # Meant to be overridden
162
- end
163
127
  end
164
128
  end
@@ -2,7 +2,6 @@ module Mutations
2
2
  class FloatFilter < InputFilter
3
3
  @default_options = {
4
4
  nils: false, # true allows an explicit nil to be valid. Overrides any other options
5
- # TODO: add strict
6
5
  min: nil, # lowest value, inclusive
7
6
  max: nil # highest value, inclusive
8
7
  }
@@ -31,13 +31,11 @@ module Mutations
31
31
  end
32
32
 
33
33
  def required(&block)
34
- # TODO: raise if nesting is wrong
35
34
  @current_inputs = @required_inputs
36
35
  instance_eval &block
37
36
  end
38
37
 
39
38
  def optional(&block)
40
- # TODO: raise if nesting is wrong
41
39
  @current_inputs = @optional_inputs
42
40
  instance_eval &block
43
41
  end
@@ -2,7 +2,6 @@ module Mutations
2
2
  class IntegerFilter < InputFilter
3
3
  @default_options = {
4
4
  nils: false, # true allows an explicit nil to be valid. Overrides any other options
5
- # TODO: add strict
6
5
  min: nil, # lowest value, inclusive
7
6
  max: nil # highest value, inclusive
8
7
  }
@@ -10,18 +10,26 @@ module Mutations
10
10
  def initialize(name, opts = {})
11
11
  super(opts)
12
12
  @name = name
13
-
14
- class_const = options[:class] || @name.to_s.camelize
15
- class_const = class_const.constantize if class_const.is_a?(String)
16
- self.options[:class] = class_const
17
-
18
- if options[:builder]
19
- options[:builder] = options[:builder].constantize if options[:builder].is_a?(String)
13
+ end
14
+
15
+ # Initialize the model class and builder
16
+ def initialize_constants!
17
+ @initialize_constants ||= begin
18
+ class_const = options[:class] || @name.to_s.camelize
19
+ class_const = class_const.constantize if class_const.is_a?(String)
20
+ self.options[:class] = class_const
21
+
22
+ if options[:builder]
23
+ options[:builder] = options[:builder].constantize if options[:builder].is_a?(String)
24
+ end
25
+
26
+ true
20
27
  end
21
28
  end
22
29
 
23
30
  def filter(data)
24
-
31
+ initialize_constants!
32
+
25
33
  # Handle nil case
26
34
  if data.nil?
27
35
  return [nil, nil] if options[:nils]
@@ -49,4 +57,4 @@ module Mutations
49
57
  end
50
58
 
51
59
  end
52
- end
60
+ end
@@ -1,19 +1,13 @@
1
1
  module Mutations
2
2
  class Outcome
3
- def initialize(is_success, result, errors)
4
- @success, @result, @errors = is_success, result, errors
3
+ attr_reader :result, :errors, :inputs
4
+
5
+ def initialize(is_success, result, errors, inputs)
6
+ @success, @result, @errors, @inputs = is_success, result, errors, inputs
5
7
  end
6
8
 
7
9
  def success?
8
10
  @success
9
11
  end
10
-
11
- def result
12
- @result
13
- end
14
-
15
- def errors
16
- @errors
17
- end
18
12
  end
19
13
  end
@@ -1,3 +1,3 @@
1
1
  module Mutations
2
- VERSION = "0.5.10"
2
+ VERSION = "0.5.11"
3
3
  end
data/spec/command_spec.rb CHANGED
@@ -72,11 +72,20 @@ describe "Command" do
72
72
  assert_raises ArgumentError do
73
73
  outcome = SimpleCommand.run(1)
74
74
  end
75
+
76
+ assert_raises ArgumentError do
77
+ outcome = SimpleCommand.run({name: "John"}, 1)
78
+ end
75
79
  end
76
80
 
77
81
  it "should accept nothing at all" do
78
82
  SimpleCommand.run # make sure nothing is raised
79
83
  end
84
+
85
+ it "should return the filtered inputs in the outcome" do
86
+ outcome = SimpleCommand.run(name: " John ", email: "john@gmail.com", amount: "5")
87
+ assert_equal ({name: "John", email: "john@gmail.com", amount: 5}).stringify_keys, outcome.inputs
88
+ end
80
89
  end
81
90
 
82
91
  describe "EigenCommand" do
data/spec/default_spec.rb CHANGED
@@ -0,0 +1,33 @@
1
+ require_relative 'spec_helper'
2
+ require 'simple_command'
3
+
4
+ describe 'Mutations - defaults' do
5
+
6
+ class DefaultCommand < Mutations::Command
7
+ required do
8
+ string :name, default: "Bob Jones"
9
+ end
10
+
11
+ def execute
12
+ inputs
13
+ end
14
+ end
15
+
16
+ it "should have a default if no value is passed" do
17
+ outcome = DefaultCommand.run
18
+ assert_equal true, outcome.success?
19
+ assert_equal ({"name" => "Bob Jones"}), outcome.result
20
+ end
21
+
22
+ it "should have the passed value if a value is passed" do
23
+ outcome = DefaultCommand.run(name: "Fred")
24
+ assert_equal true, outcome.success?
25
+ assert_equal ({"name" => "Fred"}), outcome.result
26
+ end
27
+
28
+ it "should be an error if nil is passed on a required field with a default" do
29
+ outcome = DefaultCommand.run(name: nil)
30
+ assert_equal false, outcome.success?
31
+ end
32
+
33
+ end
@@ -27,21 +27,24 @@ describe "Mutations::ModelFilter" do
27
27
  # it "disallows different types of models" do
28
28
  # end
29
29
 
30
- it "raises an exception during initialization if constantization fails" do
30
+ it "raises an exception during filtering if constantization fails" do
31
+ f = Mutations::ModelFilter.new(:complex_model)
31
32
  assert_raises NameError do
32
- Mutations::ModelFilter.new(:complex_model)
33
+ f.filter(nil)
33
34
  end
34
35
  end
35
36
 
36
- it "raises an exception during initialization if constantization of class fails" do
37
+ it "raises an exception during filtering if constantization of class fails" do
38
+ f = Mutations::ModelFilter.new(:simple_model, class: "ComplexModel")
37
39
  assert_raises NameError do
38
- Mutations::ModelFilter.new(:simple_model, class: "ComplexModel")
40
+ f.filter(nil)
39
41
  end
40
42
  end
41
43
 
42
- it "raises an exception during initialization if constantization of builder fails" do
44
+ it "raises an exception during filtering if constantization of builder fails" do
45
+ f = Mutations::ModelFilter.new(:simple_model, builder: "ComplexModel")
43
46
  assert_raises NameError do
44
- Mutations::ModelFilter.new(:simple_model, builder: "ComplexModel")
47
+ f.filter(nil)
45
48
  end
46
49
  end
47
50
 
@@ -89,4 +92,4 @@ describe "Mutations::ModelFilter" do
89
92
  # it "makes sure that if you build a record from a hash, it still has to be of the right class" do
90
93
  # end
91
94
 
92
- end
95
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.10
4
+ version: 0.5.11
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-28 00:00:00.000000000 Z
12
+ date: 2013-03-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  version: '0'
124
124
  requirements: []
125
125
  rubyforge_project:
126
- rubygems_version: 1.8.24
126
+ rubygems_version: 1.8.23
127
127
  signing_key:
128
128
  specification_version: 3
129
129
  summary: Compose your business logic into commands that sanitize and validate input.