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 +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
|