redtape 0.0.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/redtape.rb +44 -42
- metadata +23 -10
- data/lib/redtape/version.rb +0 -3
- data/spec/form_spec.rb +0 -134
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
|
-
|
13
|
-
class_attribute :model_accessors
|
28
|
+
def_delegator :@factory, :model
|
14
29
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
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
|
-
|
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
|
-
|
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
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
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
|
+
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:
|
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:
|
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: -
|
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: -
|
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: []
|
data/lib/redtape/version.rb
DELETED
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
|