active_presenter 0.0.3 → 0.0.5
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/README +8 -2
- data/lib/active_presenter/base.rb +19 -5
- data/lib/active_presenter/version.rb +1 -1
- data/test/base_test.rb +58 -30
- data/test/test_helper.rb +36 -0
- metadata +3 -3
data/README
CHANGED
@@ -25,9 +25,9 @@ Or get the source from github:
|
|
25
25
|
Creating a presenter is as simple as subclassing ActivePresenter::Base. Use the presents method to indicate which models the presenter should present.
|
26
26
|
|
27
27
|
class SignupPresenter < ActivePresenter::Base
|
28
|
-
presents
|
28
|
+
presents :user, :account
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
=== Instantiation
|
32
32
|
|
33
33
|
Then, you can instantiate the presenter using either, or both of two forms.
|
@@ -66,6 +66,12 @@ Both of these methods are compatible with error_messages_for. It just depends wh
|
|
66
66
|
|
67
67
|
You can save your presenter the same way you'd save an ActiveRecord object. Both #save, and #save! behave the same way they do on a normal AR model.
|
68
68
|
|
69
|
+
=== Callbacks
|
70
|
+
|
71
|
+
Callbacks work exactly like ActiveRecord callbacks. before_save, and after_save are available.
|
72
|
+
|
73
|
+
Note that if any of your after_save callbacks return false, the rest of them will not be run. This is consistent with AR behavior.
|
74
|
+
|
69
75
|
== Credits
|
70
76
|
|
71
77
|
ActivePresenter was created, and is maintained by {Daniel Haran}[http://danielharan.com] and {James Golick}[http://jamesgolick.com] on the train ride to {RubyFringe}[http://rubyfringe.com] from Montreal.
|
@@ -2,6 +2,9 @@ module ActivePresenter
|
|
2
2
|
# Base class for presenters. See README for usage.
|
3
3
|
#
|
4
4
|
class Base
|
5
|
+
include ActiveSupport::Callbacks
|
6
|
+
define_callbacks :before_save, :after_save
|
7
|
+
|
5
8
|
class_inheritable_accessor :presented
|
6
9
|
self.presented = {}
|
7
10
|
|
@@ -9,7 +12,7 @@ module ActivePresenter
|
|
9
12
|
# i.e.
|
10
13
|
#
|
11
14
|
# class SignupPresenter < ActivePresenter::Base
|
12
|
-
# presents
|
15
|
+
# presents :user, :account
|
13
16
|
# end
|
14
17
|
#
|
15
18
|
#
|
@@ -21,7 +24,7 @@ module ActivePresenter
|
|
21
24
|
send(t).errors
|
22
25
|
end
|
23
26
|
|
24
|
-
presented[t] = t.to_s.classify.constantize
|
27
|
+
presented[t] = t.to_s.tableize.classify.constantize
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
@@ -101,12 +104,14 @@ module ActivePresenter
|
|
101
104
|
saved = false
|
102
105
|
|
103
106
|
ActiveRecord::Base.transaction do
|
104
|
-
if valid?
|
107
|
+
if valid? && run_callbacks_with_halt(:before_save)
|
105
108
|
saved = presented_instances.map { |i| i.save(false) }.all?
|
106
109
|
raise ActiveRecord::Rollback unless saved # TODO: Does this happen implicitly?
|
107
110
|
end
|
108
111
|
end
|
109
112
|
|
113
|
+
run_callbacks_with_halt(:after_save) if saved
|
114
|
+
|
110
115
|
saved
|
111
116
|
end
|
112
117
|
|
@@ -115,10 +120,14 @@ module ActivePresenter
|
|
115
120
|
# Returns true on success, will raise otherwise.
|
116
121
|
#
|
117
122
|
def save!
|
123
|
+
raise ActiveRecord::RecordNotSaved unless run_callbacks_with_halt(:before_save)
|
124
|
+
|
118
125
|
ActiveRecord::Base.transaction do
|
119
126
|
valid? # collect errors before potential exception raise
|
120
127
|
presented_instances.each { |i| i.save! }
|
121
128
|
end
|
129
|
+
|
130
|
+
run_callbacks_with_halt(:after_save)
|
122
131
|
end
|
123
132
|
|
124
133
|
# Update attributes, and save the presentables
|
@@ -141,7 +150,7 @@ module ActivePresenter
|
|
141
150
|
end
|
142
151
|
|
143
152
|
def presentable_for(method_name)
|
144
|
-
presented.keys.detect do |type|
|
153
|
+
presented.keys.sort_by { |k| k.to_s.size }.reverse.detect do |type|
|
145
154
|
method_name.to_s.starts_with?(attribute_prefix(type))
|
146
155
|
end
|
147
156
|
end
|
@@ -167,8 +176,13 @@ module ActivePresenter
|
|
167
176
|
|
168
177
|
def attribute_protected?(name)
|
169
178
|
presentable = presentable_for(name)
|
179
|
+
return false unless presentable
|
170
180
|
flat_attribute = {flatten_attribute_name(name, presentable) => ''} #remove_att... normally takes a hash, so we use a ''
|
171
|
-
presentable.to_s.classify.constantize.new.send(:remove_attributes_protected_from_mass_assignment, flat_attribute).empty?
|
181
|
+
presentable.to_s.tableize.classify.constantize.new.send(:remove_attributes_protected_from_mass_assignment, flat_attribute).empty?
|
182
|
+
end
|
183
|
+
|
184
|
+
def run_callbacks_with_halt(callback)
|
185
|
+
run_callbacks(callback) { |result, object| result == false }
|
172
186
|
end
|
173
187
|
end
|
174
188
|
end
|
data/test/base_test.rb
CHANGED
@@ -4,76 +4,76 @@ Expectations do
|
|
4
4
|
expect :user => User, :account => Account do
|
5
5
|
SignupPresenter.presented
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
expect User.create!(hash_for_user) do |u|
|
9
9
|
SignupPresenter.new(:user => u.expected).user
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
expect User do
|
13
13
|
SignupPresenter.new.user
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
expect User.any_instance.to.receive(:login=).with('james') do
|
17
17
|
SignupPresenter.new(:user_login => 'james')
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
# admin= should be protected from mass assignment
|
21
21
|
expect SignupPresenter.new.to.be.attribute_protected?(:user_admin)
|
22
22
|
expect SignupPresenter.new(:user_admin => true).user.not.to.be.admin?
|
23
|
-
|
23
|
+
|
24
24
|
expect 'mymockvalue' do
|
25
25
|
User.any_instance.stubs(:login).returns('mymockvalue')
|
26
26
|
SignupPresenter.new.user_login
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
expect User.any_instance.to.receive(:login=).with('mymockvalue') do
|
30
30
|
SignupPresenter.new.user_login = 'mymockvalue'
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
expect SignupPresenter.new.not.to.be.valid?
|
34
34
|
expect SignupPresenter.new(:user => User.new(hash_for_user)).to.be.valid?
|
35
|
-
|
35
|
+
|
36
36
|
expect ActiveRecord::Errors do
|
37
37
|
s = SignupPresenter.new
|
38
38
|
s.valid?
|
39
39
|
s.errors
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
expect ActiveRecord::Errors do
|
43
43
|
s = SignupPresenter.new
|
44
44
|
s.valid?
|
45
45
|
s.user_errors
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
expect ActiveRecord::Errors do
|
49
49
|
s = SignupPresenter.new
|
50
50
|
s.valid?
|
51
51
|
s.account_errors
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
expect String do
|
55
55
|
s = SignupPresenter.new
|
56
56
|
s.valid?
|
57
57
|
s.errors.on(:user_login)
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
expect ActiveRecord::Base.to.receive(:transaction) do
|
61
61
|
s = SignupPresenter.new
|
62
62
|
s.save
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
expect User.any_instance.to.receive(:save) do
|
66
66
|
s = SignupPresenter.new :user => User.new(hash_for_user)
|
67
67
|
s.save
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
expect Account.any_instance.to.receive(:save) do
|
71
71
|
s = SignupPresenter.new :user => User.new(hash_for_user)
|
72
72
|
s.save
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
expect SignupPresenter.new.not.to.be.save
|
76
|
-
|
76
|
+
|
77
77
|
expect ActiveRecord::Rollback do
|
78
78
|
ActiveRecord::Base.stubs(:transaction).yields
|
79
79
|
User.any_instance.stubs(:save).returns(false)
|
@@ -81,72 +81,100 @@ Expectations do
|
|
81
81
|
s = SignupPresenter.new :user => User.new(hash_for_user)
|
82
82
|
s.save
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
expect ActiveRecord::Base.to.receive(:transaction) do
|
86
86
|
s = SignupPresenter.new
|
87
87
|
s.save!
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
expect User.any_instance.to.receive(:save!) do
|
91
91
|
s = SignupPresenter.new
|
92
92
|
s.save!
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
expect Account.any_instance.to.receive(:save!) do
|
96
96
|
User.any_instance.stubs(:save!)
|
97
97
|
s = SignupPresenter.new
|
98
98
|
s.save!
|
99
99
|
end
|
100
|
-
|
100
|
+
|
101
101
|
expect ActiveRecord::RecordInvalid do
|
102
102
|
SignupPresenter.new.save!
|
103
103
|
end
|
104
|
-
|
104
|
+
|
105
105
|
expect SignupPresenter.new(:user => User.new(hash_for_user)).to.be.save!
|
106
|
-
|
106
|
+
|
107
107
|
expect SignupPresenter.new.to.be.respond_to?(:user_login)
|
108
108
|
expect SignupPresenter.new.to.be.respond_to?(:user_password_confirmation)
|
109
109
|
expect SignupPresenter.new.to.be.respond_to?(:valid?) # just making sure i didn't break everything :)
|
110
|
-
|
110
|
+
|
111
111
|
expect User.create!(hash_for_user).not.to.be.login_changed? do |user|
|
112
112
|
s = SignupPresenter.new(:user => user)
|
113
113
|
s.update_attributes :user_login => 'Something Totally Different'
|
114
114
|
end
|
115
|
-
|
115
|
+
|
116
116
|
expect SignupPresenter.new(:user => User.create!(hash_for_user)).to.receive(:save) do |s|
|
117
117
|
s.update_attributes :user_login => 'Something'
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
expect 'Something Different' do
|
121
121
|
s = SignupPresenter.new
|
122
122
|
s.update_attributes :user_login => 'Something Different'
|
123
123
|
s.user_login
|
124
124
|
end
|
125
|
-
|
125
|
+
|
126
126
|
# this is a regression test to make sure that _title is working. we had a weird conflict with using String#delete
|
127
127
|
expect 'something' do
|
128
128
|
s = SignupPresenter.new :account_title => 'something'
|
129
129
|
s.account_title
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
expect String do
|
133
133
|
s = SignupPresenter.new
|
134
134
|
s.save
|
135
135
|
s.errors.on(:user_login)
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
expect String do
|
139
139
|
s = SignupPresenter.new
|
140
140
|
s.save! rescue
|
141
141
|
s.errors.on(:user_login)
|
142
142
|
end
|
143
|
-
|
143
|
+
|
144
144
|
expect 'Login' do
|
145
145
|
SignupPresenter.human_attribute_name(:user_login)
|
146
146
|
end
|
147
|
-
|
147
|
+
|
148
148
|
# it was raising with nil
|
149
149
|
expect SignupPresenter do
|
150
150
|
SignupPresenter.new(nil)
|
151
151
|
end
|
152
|
+
|
153
|
+
expect EndingWithSPresenter.new.address.not.to.be.nil?
|
154
|
+
|
155
|
+
# this should act as ActiveRecord models do
|
156
|
+
expect NoMethodError do
|
157
|
+
SignupPresenter.new({:i_dont_exist=>"blah"})
|
158
|
+
end
|
159
|
+
|
160
|
+
expect CantSavePresenter.new.not.to.be.save # it won't save because the filter chain will halt
|
161
|
+
|
162
|
+
expect ActiveRecord::RecordNotSaved do
|
163
|
+
CantSavePresenter.new.save!
|
164
|
+
end
|
165
|
+
|
166
|
+
expect 'Some Street' do
|
167
|
+
p = AfterSavePresenter.new
|
168
|
+
p.save
|
169
|
+
p.address.street
|
170
|
+
end
|
171
|
+
|
172
|
+
expect 'Some Street' do
|
173
|
+
p = AfterSavePresenter.new
|
174
|
+
p.save!
|
175
|
+
p.address.street
|
176
|
+
end
|
177
|
+
|
178
|
+
expect SamePrefixPresenter.new.to.be.respond_to?(:account_title)
|
179
|
+
expect SamePrefixPresenter.new.to.be.respond_to?(:account_info_info)
|
152
180
|
end
|
data/test/test_helper.rb
CHANGED
@@ -19,6 +19,14 @@ ActiveRecord::Schema.define(:version => 0) do
|
|
19
19
|
t.string :subdomain, :default => ''
|
20
20
|
t.string :title, :default => ''
|
21
21
|
end
|
22
|
+
|
23
|
+
create_table :addresses do |t|
|
24
|
+
t.string :street
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table :account_infos do |t|
|
28
|
+
t.string :info
|
29
|
+
end
|
22
30
|
end
|
23
31
|
|
24
32
|
class User < ActiveRecord::Base
|
@@ -27,11 +35,39 @@ class User < ActiveRecord::Base
|
|
27
35
|
attr_accessor :password_confirmation
|
28
36
|
end
|
29
37
|
class Account < ActiveRecord::Base; end
|
38
|
+
class Address < ActiveRecord::Base; end
|
39
|
+
class AccountInfo < ActiveRecord::Base; end
|
30
40
|
|
31
41
|
class SignupPresenter < ActivePresenter::Base
|
32
42
|
presents :account, :user
|
33
43
|
end
|
34
44
|
|
45
|
+
class EndingWithSPresenter < ActivePresenter::Base
|
46
|
+
presents :address
|
47
|
+
end
|
48
|
+
|
49
|
+
class CantSavePresenter < ActivePresenter::Base
|
50
|
+
presents :address
|
51
|
+
|
52
|
+
before_save :halt
|
53
|
+
|
54
|
+
def halt; false; end
|
55
|
+
end
|
56
|
+
|
57
|
+
class AfterSavePresenter < ActivePresenter::Base
|
58
|
+
presents :address
|
59
|
+
|
60
|
+
after_save :set_street
|
61
|
+
|
62
|
+
def set_street
|
63
|
+
address.street = 'Some Street'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class SamePrefixPresenter < ActivePresenter::Base
|
68
|
+
presents :account, :account_info
|
69
|
+
end
|
70
|
+
|
35
71
|
def hash_for_user(opts = {})
|
36
72
|
{:login => 'jane', :password => 'seekrit' }.merge(opts)
|
37
73
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_presenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Golick & Daniel Haran
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-11-21 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -56,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
56
|
requirements: []
|
57
57
|
|
58
58
|
rubyforge_project: active_presenter
|
59
|
-
rubygems_version: 1.
|
59
|
+
rubygems_version: 1.3.1
|
60
60
|
signing_key:
|
61
61
|
specification_version: 2
|
62
62
|
summary: ActivePresenter is the presenter library you already know! (...if you know ActiveRecord)
|