redtape 0.0.8 → 1.0.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.
data/lib/redtape.rb CHANGED
@@ -1,40 +1,38 @@
1
1
  require "redtape/version"
2
+ require "redtape/attribute_whitelist"
3
+ require "redtape/model_factory"
4
+ require "redtape/populator/abstract"
5
+ require "redtape/populator/root"
6
+ require "redtape/populator/has_many"
7
+ require "redtape/populator/has_one"
2
8
 
3
9
  require 'active_model'
4
10
  require 'active_support/core_ext/class/attribute'
5
11
 
12
+ require 'active_record'
13
+
14
+ require 'forwardable'
15
+
6
16
  module Redtape
17
+
18
+ class DuelingBanjosError < StandardError; end
19
+ class WhitelistViolationError < StandardError; end
20
+
7
21
  class Form
22
+ extend Forwardable
8
23
  extend ActiveModel::Naming
24
+ include ActiveModel::Callbacks
9
25
  include ActiveModel::Conversion
10
26
  include ActiveModel::Validations
11
27
 
12
- validate :models_correct
13
- class_attribute :model_accessors
28
+ def_delegator :@factory, :model
14
29
 
15
- def self.validates_and_saves(*args)
16
- attr_accessor *args
17
- self.model_accessors = args
18
- end
19
-
20
- def initialize(attrs = {})
21
- attrs.each do |k, v|
22
- send("#{k}=", v)
30
+ def initialize(controller, args = {})
31
+ if controller.respond_to?(:populate_individual_record) && args[:whitelisted_attrs]
32
+ fail DuelingBanjosError, "Redtape::Form does not accept both #{controller.class}#populate_individual_record and the 'whitelisted_attrs' argument"
23
33
  end
24
- end
25
34
 
26
- def models_correct
27
- populate
28
- self.class.model_accessors.each do |accessor|
29
- begin
30
- model = send(accessor)
31
- if model.invalid?
32
- own_your_errors_in(model)
33
- end
34
- rescue NoMethodError => e
35
- fail NoMethodError, "#{self.class} is missing 'validates_and_saves :#{accessor}': #{e}"
36
- end
37
- end
35
+ @factory = ModelFactory.new(factory_args_for(controller, args))
38
36
  end
39
37
 
40
38
  # Forms are never themselves persisted
@@ -42,34 +40,38 @@ module Redtape
42
40
  false
43
41
  end
44
42
 
43
+ def valid?
44
+ model = @factory.populate_model
45
+ valid = model.valid?
46
+
47
+ # @errors comes from ActiveModel::Validations. This may not
48
+ # be a legit hook.
49
+ @errors = model.errors
50
+
51
+ valid
52
+ end
53
+
45
54
  def save
46
55
  if valid?
47
- persist!
56
+ begin
57
+ ActiveRecord::Base.transaction do
58
+ @factory.save!
59
+ end
60
+ rescue ActiveRecord::RecordInvalid
61
+ # This shouldn't even happen with the #valid? above.
62
+ end
48
63
  else
49
64
  false
50
65
  end
51
66
  end
52
67
 
53
- def persist!
54
- self.class.model_accessors.each do |accessor|
55
- model = send(accessor)
56
- unless model.save
57
- return false
58
- end
59
- end
60
- true
61
- end
62
-
63
- def populate
64
- fail NotImplementedError, "Implement #populate in your subclass"
65
- end
66
-
67
68
  private
68
69
 
69
- def own_your_errors_in(model)
70
- model.errors.each do |k, v|
71
- errors.add(k, v)
72
- end
70
+ def factory_args_for(controller, args)
71
+ args.dup.merge(
72
+ :attrs => controller.params,
73
+ :controller => controller
74
+ )
73
75
  end
74
76
  end
75
77
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redtape
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 1.0.0
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: 2012-11-12 00:00:00.000000000 Z
12
+ date: 2012-11-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: virtus
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: rspec
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -44,7 +60,7 @@ dependencies:
44
60
  - !ruby/object:Gem::Version
45
61
  version: '0'
46
62
  - !ruby/object:Gem::Dependency
47
- name: rails
63
+ name: sqlite3
48
64
  requirement: !ruby/object:Gem::Requirement
49
65
  none: false
50
66
  requirements:
@@ -60,7 +76,7 @@ dependencies:
60
76
  - !ruby/object:Gem::Version
61
77
  version: '0'
62
78
  - !ruby/object:Gem::Dependency
63
- name: debugger
79
+ name: pry
64
80
  requirement: !ruby/object:Gem::Requirement
65
81
  none: false
66
82
  requirements:
@@ -115,8 +131,6 @@ extensions: []
115
131
  extra_rdoc_files: []
116
132
  files:
117
133
  - lib/redtape.rb
118
- - lib/redtape/version.rb
119
- - spec/form_spec.rb
120
134
  homepage: http://github.com/ClearFit/redtape
121
135
  licenses: []
122
136
  post_install_message:
@@ -131,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
145
  version: '0'
132
146
  segments:
133
147
  - 0
134
- hash: -1709053097785266848
148
+ hash: -2628988932070424880
135
149
  required_rubygems_version: !ruby/object:Gem::Requirement
136
150
  none: false
137
151
  requirements:
@@ -140,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
154
  version: '0'
141
155
  segments:
142
156
  - 0
143
- hash: -1709053097785266848
157
+ hash: -2628988932070424880
144
158
  requirements: []
145
159
  rubyforge_project:
146
160
  rubygems_version: 1.8.24
@@ -150,5 +164,4 @@ summary: Redtape provides an alternative to [ActiveRecord::NestedAttributes#acce
150
164
  in the form of, well, a Form! The initial implementation was heavily inspired by
151
165
  ["7 Ways to Decompose Fat Activerecord Models"](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/)
152
166
  by [Bryan Helmkamp](https://github.com/brynary).
153
- test_files:
154
- - spec/form_spec.rb
167
+ test_files: []
@@ -1,3 +0,0 @@
1
- module Redtape
2
- VERSION = "0.0.8"
3
- end
data/spec/form_spec.rb DELETED
@@ -1,134 +0,0 @@
1
- require 'virtus'
2
-
3
- require 'redtape'
4
- require 'active_model'
5
-
6
- class TestUser
7
- include Virtus
8
-
9
- extend ActiveModel::Naming
10
- include ActiveModel::Validations
11
- include ActiveModel::Conversion
12
-
13
- attribute :name, String
14
-
15
- validates_presence_of :name
16
- validate :name_contains_at_least_two_parts
17
-
18
- def name_contains_at_least_two_parts
19
- unless name =~ /.+ .+/
20
- errors.add(:name, "should contain at least two parts")
21
- end
22
- end
23
-
24
- def persisted?
25
- valid?
26
- end
27
-
28
- def save
29
- valid?
30
- end
31
- end
32
-
33
- class TestRegistrationForm < Redtape::Form
34
- validates_and_saves :test_user
35
-
36
- attr_accessor :test_user
37
-
38
- attr_accessor :first_name, :last_name
39
-
40
- def populate
41
- self.test_user = TestUser.new(:name => "#{first_name} #{last_name}")
42
- end
43
- end
44
-
45
- describe Redtape::Form do
46
- context "given a Form where the Form fields are a proper subset of the modeled fields" do
47
- context "where across all involved objects" do
48
- context "all field names are unique" do
49
- context "and the data is invalid" do
50
- context "in a root object" do
51
- it "reports an error on the model as <field_name>"
52
- end
53
- context "in a nested belongs_to/has_one" do
54
- it "reports an error on the model as <model_name>_<field_name>"
55
- end
56
- context "in a nested has_many" do
57
- it "reports an error on the model as <model_name>_<field_name>"
58
- end
59
- end
60
- end
61
-
62
- context "some field names overlap" do
63
- end
64
- end
65
- end
66
-
67
- context "given a Form accepting a first and last name that creates a User" do
68
- context "with valid data" do
69
- subject {
70
- TestRegistrationForm.new(
71
- :first_name => "Evan",
72
- :last_name => "Light"
73
- )
74
- }
75
-
76
- context "after saving the form" do
77
- before do
78
- subject.save
79
- end
80
-
81
- specify { subject.should be_valid }
82
- specify { subject.test_user.should be_valid }
83
- specify { subject.test_user.should be_persisted }
84
- end
85
-
86
- context "after validating the form" do
87
- before do
88
- subject.valid?
89
- end
90
-
91
- specify { subject.test_user.should be_valid }
92
- end
93
- end
94
-
95
- context "with invalid data" do
96
- subject {
97
- TestRegistrationForm.new.tap do |f|
98
- f.first_name = "Evan"
99
- end
100
- }
101
-
102
- context "after saving the form" do
103
- before do
104
- subject.save
105
- end
106
-
107
- specify { subject.should_not be_valid }
108
- specify { subject.should_not be_persisted }
109
- specify { subject.errors.should have_key(:name) }
110
- specify { subject.test_user.should_not be_valid }
111
- end
112
- end
113
- end
114
-
115
- context "given another Form subclass" do
116
- before do
117
- Class.new(Redtape::Form) do
118
- validates_and_saves :test_object
119
- end.new(:test_object => :foo)
120
- end
121
-
122
- subject {
123
- TestRegistrationForm.new
124
- }
125
-
126
- context "TestRegistrationForm still saves User" do
127
- before do
128
- subject.save
129
- end
130
-
131
- specify { subject.should_not be_valid }
132
- end
133
- end
134
- end