active_presenter 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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 User, Account
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 User, Account
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
@@ -2,7 +2,7 @@ module ActivePresenter
2
2
  module VERSION
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 3
5
+ TINY = 5
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  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.3
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-08-06 00:00:00 -04:00
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.2.0
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)