frill 0.1.10 → 0.1.11
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/app/frills/view_context_frill.rb +2 -2
- data/lib/frill.rb +1 -0
- data/lib/frill/frill.rb +4 -6
- data/lib/frill/rspec.rb +32 -0
- data/readme.markdown +153 -23
- metadata +39 -13
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.11
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module ViewContextFrill
|
2
2
|
include Frill
|
3
3
|
|
4
|
-
def self.frill? object,
|
4
|
+
def self.frill? object, context
|
5
5
|
object.class_eval do
|
6
6
|
define_method :helpers do
|
7
|
-
@frill_helper ||=
|
7
|
+
@frill_helper ||= context.respond_to?(:view_context) ? context.view_context : context
|
8
8
|
end
|
9
9
|
|
10
10
|
define_method :h do
|
data/lib/frill.rb
CHANGED
data/lib/frill/frill.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Frill
|
2
2
|
class CyclicDependency < RuntimeError; end
|
3
3
|
|
4
|
-
def self.included
|
4
|
+
def self.included base
|
5
5
|
self.dependency_graph.add base
|
6
6
|
base.extend ClassMethods
|
7
7
|
end
|
@@ -101,7 +101,7 @@ module Frill
|
|
101
101
|
new(nodes).detect!
|
102
102
|
end
|
103
103
|
|
104
|
-
def initialize
|
104
|
+
def initialize nodes
|
105
105
|
@nodes = nodes
|
106
106
|
@visited = {}
|
107
107
|
end
|
@@ -112,19 +112,17 @@ module Frill
|
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
|
-
|
116
115
|
private
|
117
|
-
|
118
116
|
attr_reader :nodes, :visited
|
119
117
|
|
120
|
-
def fan_out
|
118
|
+
def fan_out node
|
121
119
|
visited[node.label] = true
|
122
120
|
|
123
121
|
fan :next, node
|
124
122
|
fan :previous, node
|
125
123
|
end
|
126
124
|
|
127
|
-
def fan
|
125
|
+
def fan direction, start_node
|
128
126
|
current_node = start_node.send direction
|
129
127
|
|
130
128
|
while current_node
|
data/lib/frill/rspec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Frill
|
2
|
+
module RSpec
|
3
|
+
module Helpers
|
4
|
+
def frill model
|
5
|
+
view_context = ApplicationController.new.view_context.tap do |context|
|
6
|
+
context.controller.request ||= ActionController::TestRequest.new
|
7
|
+
context.request ||= context.controller.request
|
8
|
+
context.params ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
Frill.decorate model, view_context
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Frill
|
18
|
+
module RSpec
|
19
|
+
module ExampleGroup
|
20
|
+
def self.included(base)
|
21
|
+
base.metadata[:type] = :frill
|
22
|
+
base.send :include, Frill::RSpec::Helpers
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
RSpec.configure do |config|
|
29
|
+
config.include Frill::RSpec::ExampleGroup, :type => :frill, :example_group => {
|
30
|
+
:file_path => /spec[\\\/]frills/
|
31
|
+
}
|
32
|
+
end
|
data/readme.markdown
CHANGED
@@ -32,11 +32,14 @@ module FooFrill
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def foo
|
35
|
-
h.
|
35
|
+
h.content_tag :b, "#{super} bar"
|
36
36
|
end
|
37
37
|
end
|
38
38
|
```
|
39
39
|
|
40
|
+
The `h` method gives you access to all of the view helpers you would normally expect to use inside a view or a helper.
|
41
|
+
It's aliased to `helpers`, so feel free to use either.
|
42
|
+
|
40
43
|
Opt objects in your controllers into frill with the `frill` method:
|
41
44
|
|
42
45
|
```ruby
|
@@ -64,7 +67,8 @@ class ApplicationController < ActionController::Base
|
|
64
67
|
end
|
65
68
|
```
|
66
69
|
|
67
|
-
Now you don't need to use the `frill` method to decorate objects. They'll be automatically decorated
|
70
|
+
Now you don't need to use the `frill` method to decorate objects. They'll be automatically decorated
|
71
|
+
before being passed off to your view.
|
68
72
|
|
69
73
|
```ruby
|
70
74
|
class FooController < ApplicationController
|
@@ -74,6 +78,31 @@ class FooController < ApplicationController
|
|
74
78
|
end
|
75
79
|
```
|
76
80
|
|
81
|
+
### 'frill' decorates individual objects _and_ collections
|
82
|
+
|
83
|
+
The `frill` helper will decorate both single objects and collections of objects. You can use it both within your controller
|
84
|
+
and within your views.
|
85
|
+
|
86
|
+
For example, inside a controller:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
class FoosController < ApplicationController
|
90
|
+
def index
|
91
|
+
@foos = frill Foo.all
|
92
|
+
end
|
93
|
+
|
94
|
+
def show
|
95
|
+
@foo = frill Foo.find(params[:id])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
Or, in a view:
|
101
|
+
|
102
|
+
```erb
|
103
|
+
<%= render frill(@foo.comments) %>
|
104
|
+
```
|
105
|
+
|
77
106
|
## A longer story
|
78
107
|
|
79
108
|
Your product manager writes the following story for you:
|
@@ -81,12 +110,12 @@ Your product manager writes the following story for you:
|
|
81
110
|
```cucumber
|
82
111
|
Feature: Consistent Timestamp Presentation
|
83
112
|
As a user
|
84
|
-
I want "created at" timestamps presented in a uniform way on the site
|
113
|
+
I want "created at" timestamps presented in a uniform, localized way on the site
|
85
114
|
So that I can easily discover the age of content on the site
|
86
115
|
|
87
116
|
Scenario: Presenting timestamps
|
88
117
|
When I navigate to a page that displays a created_at timestamp
|
89
|
-
Then I should see that timestamp marked up as bold and formatted as follows: YYYY
|
118
|
+
Then I should see that timestamp marked up as bold and formatted for the client's locale as follows: Month DD, YYYY HH:MM
|
90
119
|
```
|
91
120
|
|
92
121
|
You see this and roll your eyes. You're thinking about all of the places that you show `created_at`
|
@@ -95,7 +124,7 @@ timestamps on the site. Reluctantly, you roll up your sleeves and start by writi
|
|
95
124
|
```ruby
|
96
125
|
module ApplicationHelper
|
97
126
|
def format_timestamp(t)
|
98
|
-
render partial: "shared/timestamp", locals: { time: t
|
127
|
+
render partial: "shared/timestamp", locals: { time: l(t, format: :long) }
|
99
128
|
end
|
100
129
|
end
|
101
130
|
```
|
@@ -127,7 +156,7 @@ Feature: Consistent Timestamp Presentation in the API
|
|
127
156
|
|
128
157
|
Scenario: Presenting timestamps
|
129
158
|
When I retrieve content with a "created_at" timestamp via the JSON API
|
130
|
-
Then I should see that timestamp formatted as follows: YYYY
|
159
|
+
Then I should see that timestamp formatted for the client's locale as follows: Month DD, YYYY HH:MM
|
131
160
|
```
|
132
161
|
|
133
162
|
You attempt to salvage the helper, updating it with concerns for the JSON format:
|
@@ -135,7 +164,7 @@ You attempt to salvage the helper, updating it with concerns for the JSON format
|
|
135
164
|
```ruby
|
136
165
|
module ApplicationHelper
|
137
166
|
def format_timestamp(t)
|
138
|
-
time = t
|
167
|
+
time = l t, format: :long
|
139
168
|
|
140
169
|
if request.format.html?
|
141
170
|
render partial: "shared/timestamp", locals: { time: time }
|
@@ -167,13 +196,15 @@ module TimestampFrill
|
|
167
196
|
end
|
168
197
|
|
169
198
|
def created_at
|
170
|
-
|
199
|
+
helpers.l super, format: :long
|
171
200
|
end
|
172
201
|
end
|
173
202
|
```
|
174
203
|
|
175
204
|
The `frill?` method tells `Frill` when to extend an object with this module. Then we redefine the `created_at` method,
|
176
|
-
calling super and then formatting the date
|
205
|
+
calling super and then formatting the date with the rails localization helper `l`. The `helpers` method is made available to
|
206
|
+
frill'ed objects; it contains the same view context that you have access to inside of views and inside of helper methods.
|
207
|
+
You can use the `h` method as well - it's simply an alias for `helpers`.
|
177
208
|
|
178
209
|
Simple enough.
|
179
210
|
|
@@ -194,13 +225,17 @@ module HtmlTimestampFrill
|
|
194
225
|
end
|
195
226
|
```
|
196
227
|
|
228
|
+
```erb
|
229
|
+
<b><%=time%></b>
|
230
|
+
```
|
231
|
+
|
197
232
|
There's three important things to note:
|
198
233
|
|
199
234
|
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`.
|
200
235
|
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.
|
201
236
|
1. The `h` method gives you access to all of the normal view helper methods you expect to you use inside your views. You can also use `helpers`.
|
202
237
|
|
203
|
-
Lastly, opt objects into frilling inside your controllers:
|
238
|
+
Lastly, opt objects into frilling inside your controllers by using the `frill` method:
|
204
239
|
|
205
240
|
```ruby
|
206
241
|
class ArticlesController < ApplicationController
|
@@ -218,7 +253,7 @@ frill will attempt to extend the object with any applicable frills (i.e., frills
|
|
218
253
|
|
219
254
|
That way, you can simply render your `created_at` attributes without any helpers, and they will automatically present themselves appropriately for their context (e.g., HTML v. JSON requests).
|
220
255
|
|
221
|
-
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:
|
256
|
+
Note that if you 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:
|
222
257
|
|
223
258
|
```ruby
|
224
259
|
class ApplicationController < ActionController::Base
|
@@ -241,31 +276,126 @@ end
|
|
241
276
|
|
242
277
|
Now, any instance variables you create in your controllers will be automatically frilled before handed off to your views.
|
243
278
|
|
244
|
-
### 'frill' decorates individual objects _and_ collections
|
245
279
|
|
246
|
-
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
|
247
|
-
and within your views.
|
248
280
|
|
249
|
-
|
281
|
+
## Testing
|
282
|
+
|
283
|
+
If you're using frill inside a Rails application, you can take advantage of the "frill" rspec helper:
|
250
284
|
|
251
285
|
```ruby
|
252
|
-
|
253
|
-
|
254
|
-
|
286
|
+
require 'spec_helper'
|
287
|
+
|
288
|
+
describe HtmlTimestampFrill do
|
289
|
+
let(:model) {
|
290
|
+
Class.new do
|
291
|
+
def created_at
|
292
|
+
DateTime.new(2012, 1, 1)
|
293
|
+
end
|
294
|
+
end.new
|
255
295
|
end
|
256
296
|
|
257
|
-
|
258
|
-
|
297
|
+
subject { frill model }
|
298
|
+
|
299
|
+
its(:created_at) { should == "<b>January 01, 2012 00:00<b>" }
|
300
|
+
end
|
301
|
+
```
|
302
|
+
|
303
|
+
It will assume an html request context, and it will embue your model with `h` and `helpers` methods.
|
304
|
+
|
305
|
+
If you're attempting to test the `MyFrill.frill?` method, you'll need to supply it with stubs:
|
306
|
+
|
307
|
+
```ruby
|
308
|
+
require 'spec_helper'
|
309
|
+
|
310
|
+
describe HtmlTimestampFrill do
|
311
|
+
let(:context) { double :view_context }
|
312
|
+
|
313
|
+
subject { HtmlTimestampFrill.frill? double(:model, created_at: "foo"), context }
|
314
|
+
|
315
|
+
context "given an HTML request" do
|
316
|
+
before { context.stub_chain(:request, :format, :html?).and_return true }
|
317
|
+
it { should be_true }
|
318
|
+
end
|
319
|
+
|
320
|
+
context "given a non-HTML request" do
|
321
|
+
before { context.stub_chain(:request, :format, :html?).and_return false }
|
322
|
+
it { should be_false }
|
259
323
|
end
|
260
324
|
end
|
261
325
|
```
|
262
326
|
|
263
|
-
|
327
|
+
Since frills are just modules, it's possible to test your frills in relative isolation.
|
264
328
|
|
265
|
-
```
|
266
|
-
|
329
|
+
```ruby
|
330
|
+
require 'spec_helper'
|
331
|
+
|
332
|
+
describe TimestampFrill do
|
333
|
+
let(:object) do
|
334
|
+
double :object,
|
335
|
+
created_at: DateTime.new(2012, 1, 1),
|
336
|
+
h: ApplicationController.new.view_context
|
337
|
+
end
|
338
|
+
|
339
|
+
subject { object.extend TimestampFrill }
|
340
|
+
|
341
|
+
its(:created_at) { should == "January 01, 2012 00:00" }
|
342
|
+
end
|
343
|
+
```
|
344
|
+
|
345
|
+
When it comes to view methods that render partials, etc., you could choose to test them with integration:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
require 'spec_helper'
|
349
|
+
|
350
|
+
class SomeModel
|
351
|
+
def h
|
352
|
+
@view_context ||= ApplicationController.new.view_context
|
353
|
+
end
|
354
|
+
|
355
|
+
def created_at
|
356
|
+
Time.new(2012,1,1)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
describe HtmlTimestampFrill do
|
361
|
+
let(:object) { SomeModel.new.extend TimestampFrill }
|
362
|
+
|
363
|
+
subject { object.extend HtmlTimestampFrill }
|
364
|
+
|
365
|
+
describe "#created_at" do
|
366
|
+
it "should render the timestamp partial" do
|
367
|
+
subject.created_at.strip.should == "<b>January 01, 2012 00:00</b>"
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
267
371
|
```
|
268
372
|
|
373
|
+
Or you could test them by stubbing out the view context, and simply setting up expectations on them:
|
374
|
+
|
375
|
+
```ruby
|
376
|
+
require 'spec_helper'
|
377
|
+
|
378
|
+
class SomeModel
|
379
|
+
def h; end
|
380
|
+
def created_at; end
|
381
|
+
end
|
382
|
+
|
383
|
+
describe HtmlTimestampFrill do
|
384
|
+
let(:object) { SomeModel.new }
|
385
|
+
|
386
|
+
subject { object.extend HtmlTimestampFrill }
|
387
|
+
|
388
|
+
describe "#created_at" do
|
389
|
+
it "should render the timestamp partial" do
|
390
|
+
subject.h.should_receive(:render)
|
391
|
+
subject.created_at
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
```
|
396
|
+
|
397
|
+
The latter can be nice if you're really just interested in testing conditional logic inside your decoration.
|
398
|
+
|
269
399
|
## Usage outside Rails
|
270
400
|
|
271
401
|
There are really just three integrations in a Rails app: the `frill`
|
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.11
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 3.2.2
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.2
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: rspec
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rspec-rails
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: capybara
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: '0'
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: pry
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
83
|
- - ! '>='
|
@@ -65,7 +85,12 @@ dependencies:
|
|
65
85
|
version: '0'
|
66
86
|
type: :development
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
69
94
|
description:
|
70
95
|
email: moonmaster9000@gmail.com
|
71
96
|
executables: []
|
@@ -75,6 +100,7 @@ files:
|
|
75
100
|
- lib/frill/engine.rb
|
76
101
|
- lib/frill/frill.rb
|
77
102
|
- lib/frill/rails.rb
|
103
|
+
- lib/frill/rspec.rb
|
78
104
|
- lib/frill.rb
|
79
105
|
- lib/generators/frill/frill_generator.rb
|
80
106
|
- lib/generators/frill/templates/frill.rb
|
@@ -106,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
132
|
version: '0'
|
107
133
|
requirements: []
|
108
134
|
rubyforge_project:
|
109
|
-
rubygems_version: 1.8.
|
135
|
+
rubygems_version: 1.8.24
|
110
136
|
signing_key:
|
111
137
|
specification_version: 3
|
112
138
|
summary: Decorating objects for presentation. Supports Rails out of the box.
|