pardner 0.1.1 → 0.1.2

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