pardner 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 035c5bfe9bac013e8796c50a45cebc257120ef29
4
- data.tar.gz: d0bacd7bfa3183c73a98b193160755151950e464
3
+ metadata.gz: 6c471837e73fc6705f1c119a78613061bfe1c5ac
4
+ data.tar.gz: 34d135125819e0f9c2daf6de081dfe155c5e4b3f
5
5
  SHA512:
6
- metadata.gz: a6862c1278b9bb0f60b9c64a51e3a680b8f3bf1794c814f9470328c8abc8ee340032534fdbf6f210df1beda103d6a5682fa6f74172172a4eadff1e7a6b659615
7
- data.tar.gz: 8c92c6d90d313131c88a21fda089564f0f064d79d5387c07a0ee1f94d1357123c3cfc5f2cf700d2fcb8a5a62e937d1949a7b4eff9f9d43f544dcf89528f9b9aa
6
+ metadata.gz: 84039fd839735f1c8455f5ad54e43e27e6e90c94a1aea57535fcb4af3fce6906a1ed6ac50833f14e57bb1ac4b1acb3c7afc2056a286a10be233bae935a36bada
7
+ data.tar.gz: 25092c748a4214a81b0d27ba6f01828e68854599f7a297638a784366679edf495b5a234f5ca5a17c756de90f84338f53f4a6af35efb061da22297a05e02bbb4d
data/README.md CHANGED
@@ -13,52 +13,114 @@
13
13
 
14
14
  # pardner
15
15
 
16
- A decorator library for ActiveRecord that has features to fit in nicely with the ActiveModel world
16
+ A decorator library for ActiveRecord that has features to fit in nicely
17
+ with the ActiveModel world
17
18
 
18
19
  ## Use cases
19
20
 
20
21
  1. Presenters for views
21
- 2. Handle form params and translate them to what the modal understands
22
- 3. Creating or updating multiple ActiveRecord models atomically
22
+ 2. Translate between form params and model attributes
23
+ 3. Save multiple ActiveRecord models atomically
23
24
  4. Adding optional validations
24
25
  5. And more!
25
26
 
26
- ## 1. Presenters
27
+ ## Usage
27
28
 
28
- A presenter can be used as an alternative to a view helper to add logic
29
- to a view. In this example we add a `description` method to a decorator
30
- for the view:
29
+ ``` ruby
30
+ # Decorate an ActiveRecord or ActiveModel class by creating a subclass
31
+ # of Pardner::Base. In this example we'll pretend a User active record
32
+ # class exists.
31
33
 
32
- # app/models/conestoga_wagon.rb
33
- class ConestogaWagon < ActiveRecord::Base
34
- attr_accessor :wheels_count, :covered
35
- end
34
+ class SilverMiner < Pardner::Base
35
+ howdy_pardner User
36
+ end
36
37
 
37
- # app/presenters/conestoga_wagon_presenter.rb
38
- class ConestogaWagonPresenter < Pardner::Base
39
- howdy_pardner ConestogaWagon
38
+ # Instantiate it by calling `.new` and passing in a User object:
39
+ miner = SilverMiner.new User.find(123)
40
+ miner.new_record? # => true
41
+ miner.id # => 123
40
42
 
41
- def description
42
- covered_string = covered ? "covered" : "uncovered"
43
- "a #{wheels_count} wheeled #{covered_string} wagon"
44
- end
45
- end
43
+ # Behavior can be added to the decorator by defining methods:
44
+ class SilverMiner < Pardner::Base
45
+ # Add the title 'Silver miner' to the user name
46
+ def name
47
+ "Silver miner #{super}"
48
+ end
49
+ end
50
+
51
+ miner.name # => 'Silver miner Sam'
52
+
53
+ # by adding callbacks:
54
+ class SilverMiner < Pardner::Base
55
+ before_destroy :retirement_party
56
+
57
+ private
46
58
 
47
- # app/view/conestoga_wagons/show.html.haml
48
- ...
49
- span.description= @conestoga_wagon.description
50
- ...
59
+ def retirement_party
60
+ years_worked = Time.now.year - self.start_year
61
+ Cake.create! candles_count: years_worked
62
+ end
63
+ end
51
64
 
52
- ## 2. Handle form params
65
+ miner.destroy # creates a Cake
53
66
 
54
- In this example, the `GoldRush` model has separate `city` and `state`
67
+ # by adding validations:
68
+ class SilverMiner < Pardner::Base
69
+ validates_inclusion_of :favorite_ore, in: ['silver']
70
+ end
71
+
72
+ miner.favorite_ore = 'gold'
73
+ miner.valid? # => false
74
+ ```
75
+
76
+ ## More examples
77
+
78
+ ### Presenters
79
+
80
+ A presenter a way to add logic to a view. It's an alternative to a view
81
+ helper. In this example a ConestogaWagon model is decorated to have a
82
+ `description` method.
83
+
84
+ ```ruby
85
+ # app/models/conestoga_wagon.rb
86
+ # The table has columns is_covered:boolean and wheels_count:integer
87
+ class ConestogaWagon < ActiveRecord::Base
88
+ end
89
+
90
+ # app/presenters/conestoga_wagon_presenter.rb
91
+ class ConestogaWagonPresenter < Pardner::Base
92
+ howdy_pardner ConestogaWagon
93
+
94
+ def description
95
+ covered_string = is_covered ? "covered" : "uncovered"
96
+ "a #{wheels_count} wheeled #{covered_string} wagon"
97
+ end
98
+ end
99
+
100
+ # app/controllers/conestoga_wagons_controller.rb
101
+ class ConestogaWagonsController < ApplicationController
102
+ def show
103
+ @wagon = ConestogaWagonPresenter.new ConestogaWagon.find(params[:id])
104
+ end
105
+ end
106
+ ```
107
+
108
+ ```haml
109
+ -# app/views/conestoga_wagons/show.html.haml
110
+ span.description= @conestoga_wagon.description
111
+ ```
112
+
113
+ ### Translating between form params and model attributes
114
+
115
+ In this example, the `GoldRush` model has separate `city` and `territory`
55
116
  fields, but we want to present that to the user as a single form field.
56
117
  The decorator will split the incoming `location` param into `city` and
57
- `state` fields.
118
+ `territory` fields, and vice versa.
58
119
 
120
+ ```ruby
59
121
  # app/models/gold_rush.rb
122
+ # The table gold_rushes has columns city:string and territory:string
60
123
  class GoldRush < ActiveRecord::Base
61
- attr_accessor :city, :state
62
124
  end
63
125
 
64
126
  # app/decorators/gold_rush_form.rb
@@ -66,22 +128,153 @@ The decorator will split the incoming `location` param into `city` and
66
128
  howdy_pardner GoldRush
67
129
 
68
130
  def location
69
- "#{city}, #{state}"
131
+ "#{city}, #{territory}"
70
132
  end
71
133
 
72
134
  def location=(val)
73
- self.city, self.state = val.split ','
135
+ self.city, self.territory = val.split ','
74
136
  end
75
137
  end
76
138
 
77
139
  # app/controllers/gold_rushes_controller.rb
78
- def new
79
- @gold_rush = GoldRushForm.new GoldRush.new
140
+ class GoldRushesController < ApplicationController
141
+ def new
142
+ @gold_rush = GoldRushForm.new GoldRush.new
143
+ end
144
+
145
+ def create
146
+ @gold_rush = GoldRushForm.new GoldRush.new
147
+ @gold_rush.attributes = params[:gold_rush]
148
+
149
+ if @gold_rush.save
150
+ flash[:notice] = "There's gold in them thar hills"
151
+ else
152
+ render :new
153
+ end
154
+ end
80
155
  end
156
+ ```
81
157
 
82
- # app/view/gold_rushes/new.html.haml
158
+ ```haml
159
+ -# app/view/gold_rushes/new.html.haml
83
160
  = form_for @gold_rush do |form|
84
161
  form.text :location
162
+ form.submit
163
+ ```
164
+
165
+ ### Saving multiple ActiveRecord models atomically
166
+
167
+ A decorator can be a convenient way to coordinate changes to several
168
+ models atomically. Model callbacks can also be used for this but have
169
+ some downsides:
170
+
171
+ * sometimes its not clear which model should have the callback,
172
+ * decorators can opt-in more easily than callbacks,
173
+ * and extensive use of callbacks can lead to infinite loops.
174
+
175
+ In this example, when a gold rush is declared a bunch of supporting
176
+ models need to be created.
177
+
178
+ ```ruby
179
+ # app/controllers/gold_rushes_controller.rb
180
+ class GoldRushesController < ApplicationController
181
+ def create
182
+ @gold_rush = GoldRushDeclared.new GoldRush.new
183
+ @gold_rush.attributes = params[:gold_rush]
184
+
185
+ if @gold_rush.save
186
+ flash[:notice] = "There's gold in them thar hills"
187
+ else
188
+ render :new
189
+ end
190
+ end
191
+ end
192
+
193
+ # app/services/gold_rush_declared.rb
194
+ class GoldRushDeclared < Pardner::Base
195
+ howdy_pardner GoldRush
196
+ before_validate :build_infrastructure
197
+ validate :mining_town_must_exist
198
+ validate :transport_must_exist
199
+
200
+ private
201
+
202
+ def build_infrastructure
203
+ if MiningTown.where(territory: self.territory).is_nearby(self).empty?
204
+ MiningTown.create! territory: self.territory, name: "Town near #{self.name}"
205
+ end
206
+
207
+ if Railroad.is_nearby(self).empty? && WagonTrail.is_nearby(self).empty?
208
+ WagonTrail.create! territory: self.territory, destination: self.location
209
+ end
210
+ end
211
+
212
+ def mining_town_must_exist
213
+ return if MiningTown.is_nearby(self)
214
+ errors.add :base, 'no mining town exists'
215
+ end
216
+
217
+ def transport_must_exist
218
+ return if Railroad.is_nearby(self) || WagonTrail.is_nearby(self)
219
+ errors.add :base, 'no transport exists'
220
+ end
221
+ end
222
+ ```
223
+
224
+ ### Adding optional validations
225
+
226
+ In this example, we have two controllers for the same resource. One
227
+ supports an admin interface and one is customer facing. We want the
228
+ customer facing one to do more validation than the admin one.
229
+
230
+ ```ruby
231
+ # app/decorators/small_posse.rb
232
+ class SmallPosse < Pardner::Base
233
+ howdy_pardner Posse
234
+ validate :must_be_small
235
+
236
+ MAX_SIZE = 5
237
+
238
+ private
239
+
240
+ def must_be_small
241
+ if deputies_count > MAX_SIZE
242
+ errors.add :deputies, "must be less than #{MAX_SIZE}"
243
+ end
244
+ end
245
+ end
246
+
247
+ # app/controllers/posses_controller.rb
248
+ class PossesController < ApplicationController
249
+ def create
250
+ # This controller is customer facing so they can only create small posses
251
+ @posse = SmallPosse.new Posse.new
252
+ @posse.attributes = params[:posse]
253
+
254
+ if @posse.save
255
+ flash[:notice] = 'Get a rope'
256
+ else
257
+ render :new
258
+ end
259
+ end
260
+ end
261
+
262
+ # app/controllers/admin/posses_controller.rb
263
+ module Admin
264
+ class PossesController < ApplicationController
265
+ def create
266
+ # This controller is for admins so they can do what they want
267
+ @posse = Posse.new params[:posse]
268
+
269
+ if @posse.save
270
+ flash[:notice] = 'Every day above ground is a good day'
271
+ else
272
+ render :new
273
+ end
274
+ end
275
+ end
276
+ end
277
+ ```
85
278
 
86
279
  ## Installation
87
280
 
@@ -99,9 +292,11 @@ Or install it yourself as:
99
292
 
100
293
  $ gem install pardner
101
294
 
102
- ## Usage
295
+ ## Similar projects
103
296
 
104
- TODO: Write usage instructions here
297
+ * draper https://github.com/drapergem/draper
298
+ * informal https://github.com/joshsusser/informal
299
+ * more https://www.ruby-toolbox.com/categories/rails_presenters
105
300
 
106
301
  ## Development
107
302
 
@@ -111,10 +306,9 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
111
306
 
112
307
  ## Contributing
113
308
 
114
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/pardner. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
309
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ajh/pardner.
115
310
 
116
311
 
117
312
  ## License
118
313
 
119
314
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
120
-
@@ -87,8 +87,11 @@ module Pardner
87
87
  def save
88
88
  valid? or return false
89
89
 
90
- status = ActiveRecord::Base.transaction do
91
- run_callbacks(:save) { super }
90
+ status = nil
91
+
92
+ ActiveRecord::Base.transaction do
93
+ status = run_callbacks(:save) { super }
94
+ status or raise ActiveRecord::Rollback
92
95
  end
93
96
 
94
97
  status == true
@@ -1,3 +1,3 @@
1
1
  module Pardner
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pardner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Hartford
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-09 00:00:00.000000000 Z
11
+ date: 2016-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -252,7 +252,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
252
252
  version: '0'
253
253
  requirements: []
254
254
  rubyforge_project:
255
- rubygems_version: 2.5.1
255
+ rubygems_version: 2.4.5
256
256
  signing_key:
257
257
  specification_version: 4
258
258
  summary: A decorator library for ActiveRecord