frill 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/frill/rails.rb +20 -0
- data/readme.markdown +114 -77
- metadata +11 -11
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.9
|
data/lib/frill/rails.rb
CHANGED
@@ -1,5 +1,25 @@
|
|
1
|
+
module Frill
|
2
|
+
module Auto
|
3
|
+
protected
|
4
|
+
|
5
|
+
def view_assigns
|
6
|
+
new_hash = {}
|
7
|
+
|
8
|
+
super.each do |key,value|
|
9
|
+
new_hash[key] = frill value
|
10
|
+
end
|
11
|
+
|
12
|
+
new_hash
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
1
17
|
module ActionController
|
2
18
|
class Base
|
19
|
+
def self.auto_frill
|
20
|
+
self.send :include, Frill::Auto
|
21
|
+
end
|
22
|
+
|
3
23
|
helper_method :frill
|
4
24
|
|
5
25
|
private
|
data/readme.markdown
CHANGED
@@ -23,66 +23,110 @@ $ rails g frill Timestamp --test-framework=rspec
|
|
23
23
|
create spec/frills/timestamp_frill_spec.rb
|
24
24
|
```
|
25
25
|
|
26
|
-
##
|
26
|
+
## Refactoring timestamp helpers with decorators
|
27
27
|
|
28
|
-
|
29
|
-
`frill` inside a Rails app. Checkout the `Usage outside Rails` section
|
30
|
-
below if you're not using Rails.)
|
28
|
+
Your product manager writes the following story for you:
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
```cucumber
|
31
|
+
Feature: Consistent Timestamp Presentation
|
32
|
+
As a user
|
33
|
+
I want "created at" timestamps presented in a uniform way on the site
|
34
|
+
So that I can easily discover the age of content on the site
|
36
35
|
|
37
|
-
|
36
|
+
Scenario: Presenting timestamps
|
37
|
+
When I navigate to a page that displays a created_at timestamp
|
38
|
+
Then I should see that timestamp marked up as bold and formatted as follows: YYYY/MM/DD
|
39
|
+
```
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
You see this and roll your eyes. You're thinking about all of the places that you show `created_at`
|
42
|
+
timestamps on the site. Regardless you roll up your sleeves and start by writing the following helper and partial:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
module ApplicationHelper
|
46
|
+
def format_timestamp(t)
|
47
|
+
render partial: "shared/timestamp", locals: { time: t.strftime "%Y/%m/%d" }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
```erb
|
53
|
+
<b><%=time%></b>
|
54
|
+
```
|
55
|
+
|
56
|
+
You then begin the tedious task of tracking down all of the places you render timestamps on the site and wrapping them with `format_timestamp` helper calls:
|
57
|
+
|
58
|
+
```erb
|
59
|
+
...
|
60
|
+
Written on <%=format_timestamp @article.created_at %>
|
44
61
|
```
|
45
62
|
|
46
|
-
You
|
47
|
-
|
63
|
+
You hate this approach.
|
64
|
+
|
65
|
+
1. It's tedious
|
66
|
+
1. It's procedural
|
67
|
+
1. Developers have to remember to manually wrap timestamps with your `format_timestamp` helper. Developers suck at remembering things like that. FACEPALM
|
68
|
+
|
69
|
+
After you deliver the story, your product owner says "Great! But what about the format of timestamps in the JSON api? Here's another story."
|
70
|
+
|
71
|
+
```cucumber
|
72
|
+
Feature: Consistent Timestamp Presentation in the API
|
73
|
+
As an API consumer
|
74
|
+
I want "created at" timestamps presented in the API in uniform way
|
75
|
+
So that I can easily discover the age of data I consume
|
76
|
+
|
77
|
+
Scenario: Presenting timestamps
|
78
|
+
When I retrieve content with a "created_at" timestamp via the JSON API
|
79
|
+
Then I should see that timestamp formatted as follows: YYYY/MM/DD
|
80
|
+
```
|
81
|
+
|
82
|
+
You attempt to salvage the helper, updating it with concerns for the JSON format:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
module ApplicationHelper
|
86
|
+
def format_timestamp(t)
|
87
|
+
time = t.strftime "%Y/%m/%d"
|
88
|
+
|
89
|
+
if request.format.html?
|
90
|
+
render partial: "shared/timestamp", locals: { time: time }
|
91
|
+
elsif request.format.json?
|
92
|
+
time
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
And now you begin the tedious track of updating all of the JSON views with the helper:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
json.created_at format_timestamp(@article.created_at)
|
102
|
+
```
|
103
|
+
|
104
|
+
At this point, you're banging your head against a table.
|
105
|
+
|
106
|
+
### Enter Frill
|
48
107
|
|
49
|
-
|
108
|
+
Let's refactor this using the decorator pattern. First, revert all of your changes. Next, add the `frill` gem to your Gemfile, run `bundle`, then generate a frill: `rails g frill TimestampFrill`:
|
50
109
|
|
51
110
|
```ruby
|
52
111
|
module TimestampFrill
|
53
112
|
include Frill
|
54
113
|
|
55
114
|
def self.frill? object, context
|
56
|
-
object.respond_to?(:created_at)
|
115
|
+
object.respond_to?(:created_at)
|
57
116
|
end
|
58
117
|
|
59
118
|
def created_at
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
def updated_at
|
64
|
-
format_time super
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def format_time(t)
|
70
|
-
t.strftime "%Y/%m/%d"
|
119
|
+
super.strftime "%Y/%m/%d"
|
71
120
|
end
|
72
121
|
end
|
73
122
|
```
|
74
123
|
|
75
|
-
The
|
76
|
-
|
124
|
+
The `frill?` method tells `Frill` when to extend an object with this module. Then we redefine the `created_at` method,
|
125
|
+
calling super and then formatting the date returned with `strftime`.
|
77
126
|
|
78
|
-
|
127
|
+
Simple enough.
|
79
128
|
|
80
|
-
|
81
|
-
$ rails g frill HtmlTimestamp --test-framework=rspec
|
82
|
-
create app/frills/html_timestamp_frill.rb
|
83
|
-
invoke rspec
|
84
|
-
create spec/frills/html_timestamp_frill_spec.rb
|
85
|
-
```
|
129
|
+
Next, generate another frill for presenting timestamps via HTML (`rails g frill HtmlTimestampFrill`):
|
86
130
|
|
87
131
|
```ruby
|
88
132
|
module HtmlTimestampFrill
|
@@ -90,37 +134,24 @@ module HtmlTimestampFrill
|
|
90
134
|
after TimestampFrill
|
91
135
|
|
92
136
|
def self.frill? object, context
|
93
|
-
object.respond_to?(:created_at) &&
|
94
|
-
object.respond_to?(:updated_at) &&
|
95
|
-
context.request.format.html?
|
137
|
+
object.respond_to?(:created_at) && context.request.format.html?
|
96
138
|
end
|
97
139
|
|
98
140
|
def created_at
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
def updated_at
|
103
|
-
format_time_for_html super
|
104
|
-
end
|
105
|
-
|
106
|
-
private
|
107
|
-
def format_time_for_html t
|
108
|
-
h.content_tag :b, t
|
141
|
+
h.render partial: "shared/timestamp", locals: { time: super }
|
109
142
|
end
|
110
143
|
end
|
111
144
|
```
|
112
145
|
|
113
|
-
|
114
|
-
objects that have timestamps _when presented in "html"_. Also, we
|
115
|
-
tell `Frill` to decorate `after` `TimestampFrill` is applied (so that
|
116
|
-
`super` in `created_at` returns our `TimestampFrill` response).
|
146
|
+
There's two important things to note:
|
117
147
|
|
118
|
-
|
148
|
+
1. This frill comes after `TimestampFrill`. That tells `Frill` that it should only attempt to extend an object with this module after attempting to extend it with `TimestampFrill`.
|
149
|
+
1. The `frill?` method only returns true if it's an HTML request, meaning this frill won't be extended onto objects for your JSON api.
|
119
150
|
|
120
|
-
|
151
|
+
Lastly, opt objects into frilling inside your controllers:
|
121
152
|
|
122
153
|
```ruby
|
123
|
-
class
|
154
|
+
class ArticlesController < ApplicationController
|
124
155
|
respond_to :json, :html
|
125
156
|
|
126
157
|
def show
|
@@ -130,19 +161,37 @@ class PostsController < ApplicationController
|
|
130
161
|
end
|
131
162
|
```
|
132
163
|
|
133
|
-
|
164
|
+
And that's it. You don't have to update any of your views. Why? When you call the `frill` method inside your controller and pass it an object (or a collection of objects),
|
165
|
+
frill will attempt to extend the object with any applicable frills (i.e., frills that return `true` for the `frill?` method when passed the object and the request context).
|
134
166
|
|
135
|
-
|
167
|
+
That way, you can simple render your `created_at` attributes without any helpers, and they will automatically present themselves appropriately for their context (e.g., HTML v. JSON requests).
|
136
168
|
|
137
|
-
|
138
|
-
|
169
|
+
Note that if prefer, you can configure your controllers to automatically frill all objects for presentation by calling the `auto_frill` method inside your `ApplicationController`, instead of manually having to opt them it via the `frill` method:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
class ApplicationController < ActionController::Base
|
173
|
+
auto_frill
|
174
|
+
end
|
139
175
|
```
|
140
176
|
|
141
|
-
|
177
|
+
Now, you could remove the `frill` from your `ArticlesController`:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
class ArticlesController < ApplicationController
|
181
|
+
respond_to :json, :html
|
182
|
+
|
183
|
+
def show
|
184
|
+
@article = Article.find(params[:id])
|
185
|
+
respond_with @article
|
186
|
+
end
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
Now, any instance variables you create in your controllers will be automatically frilled before handed off to your views.
|
142
191
|
|
143
192
|
### 'frill' decorates individual objects _and_ collections
|
144
193
|
|
145
|
-
|
194
|
+
As I've hinted at, the `frill` helper will decorate both single objects and collections of objects. You can use it both within your controller
|
146
195
|
and within your views.
|
147
196
|
|
148
197
|
For example, inside a controller:
|
@@ -169,7 +218,7 @@ Or, in a view:
|
|
169
218
|
|
170
219
|
There are really just two integrations in a Rails app: the `frill`
|
171
220
|
method inside of your controller, plus the ability to call
|
172
|
-
|
221
|
+
helper methods inside of your module methods.
|
173
222
|
|
174
223
|
To kickoff the decoration of an object outside of a Rails application,
|
175
224
|
simply call `Frill.decorate`:
|
@@ -182,15 +231,3 @@ Frill.decorate my_object, my_context
|
|
182
231
|
|
183
232
|
* Ben Moss
|
184
233
|
* Nicholas Greenfield
|
185
|
-
|
186
|
-
## License
|
187
|
-
|
188
|
-
(The MIT License)
|
189
|
-
|
190
|
-
Copyright © 2012 Matt Parker
|
191
|
-
|
192
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
193
|
-
|
194
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
195
|
-
|
196
|
-
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: frill
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-08-25 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement: &
|
16
|
+
requirement: &70096022400640 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.2.2
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70096022400640
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70096022399720 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70096022399720
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec-rails
|
38
|
-
requirement: &
|
38
|
+
requirement: &70096022399120 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70096022399120
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: capybara
|
49
|
-
requirement: &
|
49
|
+
requirement: &70096022398500 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70096022398500
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: pry
|
60
|
-
requirement: &
|
60
|
+
requirement: &70096022397820 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70096022397820
|
69
69
|
description:
|
70
70
|
email: moonmaster9000@gmail.com
|
71
71
|
executables: []
|