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 +4 -4
- data/README.md +230 -36
- data/lib/pardner/base.rb +5 -2
- data/lib/pardner/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c471837e73fc6705f1c119a78613061bfe1c5ac
|
4
|
+
data.tar.gz: 34d135125819e0f9c2daf6de081dfe155c5e4b3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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.
|
22
|
-
3.
|
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
|
-
##
|
27
|
+
## Usage
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
34
|
+
class SilverMiner < Pardner::Base
|
35
|
+
howdy_pardner User
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
65
|
+
miner.destroy # creates a Cake
|
53
66
|
|
54
|
-
|
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
|
-
`
|
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}, #{
|
131
|
+
"#{city}, #{territory}"
|
70
132
|
end
|
71
133
|
|
72
134
|
def location=(val)
|
73
|
-
self.city, self.
|
135
|
+
self.city, self.territory = val.split ','
|
74
136
|
end
|
75
137
|
end
|
76
138
|
|
77
139
|
# app/controllers/gold_rushes_controller.rb
|
78
|
-
|
79
|
-
|
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
|
-
|
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
|
-
##
|
295
|
+
## Similar projects
|
103
296
|
|
104
|
-
|
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/
|
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
|
-
|
data/lib/pardner/base.rb
CHANGED
@@ -87,8 +87,11 @@ module Pardner
|
|
87
87
|
def save
|
88
88
|
valid? or return false
|
89
89
|
|
90
|
-
status =
|
91
|
-
|
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
|
data/lib/pardner/version.rb
CHANGED
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.
|
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-
|
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
|
255
|
+
rubygems_version: 2.4.5
|
256
256
|
signing_key:
|
257
257
|
specification_version: 4
|
258
258
|
summary: A decorator library for ActiveRecord
|