rails_decorators 0.1.1 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +27 -34
- data/lib/generators/decorator/model/USAGE +1 -2
- data/lib/generators/decorator/model/model_generator.rb +2 -2
- data/lib/generators/decorator/model/templates/model.rb +13 -0
- data/lib/generators/decorator/setup/setup_generator.rb +5 -5
- data/lib/rails_decorators.rb +1 -1
- data/lib/rails_decorators/base.rb +17 -0
- metadata +5 -6
- data/lib/rails_decorators/loader.rb +0 -22
data/README.markdown
CHANGED
@@ -15,12 +15,10 @@ In general, a decorator wraps an object with presentation-related accessor metho
|
|
15
15
|
|
16
16
|
To implement the pattern in Rails we can:
|
17
17
|
|
18
|
-
1. Write a
|
19
|
-
2.
|
18
|
+
1. Write a wrapper class with the decoration methods
|
19
|
+
2. Wrap the data object
|
20
20
|
3. Utilize those methods within our view layer
|
21
21
|
|
22
|
-
We're not polluting the model layer because it's code stays purely persistence and business logic. The decorator is a part of the view layer, and that's the only place we'd utilize the decoration methods.
|
23
|
-
|
24
22
|
## How do you utilize this gem in your application?
|
25
23
|
|
26
24
|
Here are the steps to utilizing this gem:
|
@@ -37,23 +35,21 @@ Run bundle:
|
|
37
35
|
bundle
|
38
36
|
```
|
39
37
|
|
40
|
-
Run the setup generator to create a decorators folder and an initializer to connect your decorators with your models:
|
41
|
-
|
42
|
-
```
|
43
|
-
rails generate decorator:setup
|
44
|
-
```
|
45
|
-
|
46
38
|
Create a decorator for your model (ex: `Article`)
|
47
39
|
|
48
40
|
```
|
49
41
|
rails generate decorator:model Article
|
50
42
|
```
|
51
43
|
|
52
|
-
Open the decorator
|
44
|
+
Open the decorator model (ex: `app/decorators/article_decorator.rb`)
|
53
45
|
|
54
|
-
Add your new formatting methods
|
46
|
+
Add your new formatting methods as normal instance or class methods. You have access to the Rails helpers from the following classes:
|
55
47
|
|
56
|
-
|
48
|
+
```
|
49
|
+
ActionView::Helpers::TagHelper
|
50
|
+
ActionView::Helpers::UrlHelper
|
51
|
+
ActionView::Helpers::TextHelper
|
52
|
+
```
|
57
53
|
|
58
54
|
Use the new methods in your views like any other model method (ex: `@article.formatted_published_at`)
|
59
55
|
|
@@ -80,21 +76,13 @@ Could we build that using a partial? Yes. A helper? Uh-huh. But the point of the
|
|
80
76
|
|
81
77
|
First, follow the steps above to add the dependency, update your bundle, then run the `rails generate decorator:setup` to prepare your app.
|
82
78
|
|
83
|
-
|
79
|
+
Since we're talking about the `Article` model we'll create an `ArticleDecorator` class. You could do it by hand, but use the provided generator:
|
84
80
|
|
85
81
|
```
|
86
82
|
rails generate decorator:model Article
|
87
83
|
```
|
88
84
|
|
89
|
-
Now open up the created `app/decorators/article_decorator.rb` and you'll find an `ArticleDecorator`
|
90
|
-
|
91
|
-
Why is this a `module`? A traditional decorator implementation uses a second class to completely encapsulate the subject. But because Ruby has such a flexible object model, this isn't necessary. We instead build the decorator as a module and inject that module into the original class. This allows the model's source code to remain purely persistence and business logic while the module contains all the decorations. Neither the model nor the controller layers need to know about the existence of the decorator methods, they can live up at the view layer.
|
92
|
-
|
93
|
-
Within the module, notice that we `extend ActiveSupport::Concern`. This library allows us to mask some of the ugliness of Ruby modules and utilize a simplified syntax. (Here's a great tutorial: http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/)
|
94
|
-
|
95
|
-
Next you'll see commented-out samples for how to utilize Rails' view helper methods. In this example we could make use of the `content_tag` helper, so uncomment the `TagHelper` line. (API Reference: http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html)
|
96
|
-
|
97
|
-
Move below those comments and you'll find a nested module named `InstanceMethods`. Any methods you add inside here will be available as instance methods on your object. Within the module, we would add this method:
|
85
|
+
Now open up the created `app/decorators/article_decorator.rb` and you'll find an `ArticleDecorator` class. You'll see commented-out samples for how to utilize Rails' view helper methods. Move below those comments and add this method:
|
98
86
|
|
99
87
|
```ruby
|
100
88
|
def formatted_published_at
|
@@ -106,25 +94,30 @@ end
|
|
106
94
|
|
107
95
|
*ASIDE*: Unfortunately, due to the current implementation of `content_tag`, you can't use the style of sending the content is as a block or you'll get an error about `undefined method 'output_buffer='`. Passing in the content as the second argument, as above, works fine.
|
108
96
|
|
109
|
-
|
97
|
+
Then you need to perform the wrapping in your controller. Here's the simplest method:
|
110
98
|
|
99
|
+
```ruby
|
100
|
+
class ArticlesController < ApplicationController
|
101
|
+
def show
|
102
|
+
@article = ArticleDecorator.new( Article.find params[:id] )
|
103
|
+
end
|
104
|
+
end
|
111
105
|
```
|
106
|
+
|
107
|
+
Then within your views you can utilize both the normal data methods and your new presentation methods:
|
108
|
+
|
109
|
+
```ruby
|
112
110
|
<%= @article.formatted_published_at %>
|
113
111
|
```
|
114
112
|
|
115
113
|
Ta-da! Object-oriented data formatting for your view layer. Below is the complete decorator with extra comments removed:
|
116
114
|
|
117
115
|
```ruby
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
def formatted_published_at
|
124
|
-
date = content_tag(:span, published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
125
|
-
time = content_tag(:span, published_at.strftime("%l:%M%p").delete(" "), :class => 'time')
|
126
|
-
content_tag :span, date + time, :class => 'published_at'
|
127
|
-
end
|
116
|
+
class ArticleDecorator < RailsDecorators::Base
|
117
|
+
def formatted_published_at
|
118
|
+
date = content_tag(:span, published_at.strftime("%A, %B %e").squeeze(" "), :class => 'date')
|
119
|
+
time = content_tag(:span, published_at.strftime("%l:%M%p"), :class => 'time').delete(" ")
|
120
|
+
content_tag :span, date + time, :class => 'created_at'
|
128
121
|
end
|
129
122
|
end
|
130
123
|
```
|
@@ -2,8 +2,8 @@ module Decorator
|
|
2
2
|
class ModelGenerator < Rails::Generators::NamedBase
|
3
3
|
source_root File.expand_path('../templates', __FILE__)
|
4
4
|
|
5
|
-
def
|
6
|
-
template '
|
5
|
+
def build_model
|
6
|
+
template 'model.rb', "app/decorators/#{singular_name}_decorator.rb"
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class <%= singular_name.camelize %>Decorator < RailsDecorators::Base
|
2
|
+
# Rails helpers like content_tag, link_to, and pluralize are already
|
3
|
+
# available to you. If you need access to other helpers, include them
|
4
|
+
# like this:
|
5
|
+
# include ActionView::Helpers::TextHelper
|
6
|
+
# Or pull in the whole kitchen sink:
|
7
|
+
# include ActionView::Helpers
|
8
|
+
|
9
|
+
# Then define presentation-related instance methods. Ex:
|
10
|
+
# def formatted_created_at
|
11
|
+
# content_tag :span, created_at.strftime("%A")
|
12
|
+
# end
|
13
|
+
end
|
@@ -6,10 +6,10 @@ module Decorator
|
|
6
6
|
empty_directory "app/decorators"
|
7
7
|
end
|
8
8
|
|
9
|
-
def build_initializer
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
9
|
+
# def build_initializer
|
10
|
+
# initializer("load_decorators.rb") do
|
11
|
+
# "RailsDecorators::Loader.load"
|
12
|
+
# end
|
13
|
+
# end
|
14
14
|
end
|
15
15
|
end
|
data/lib/rails_decorators.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require 'rails_decorators/
|
1
|
+
require 'rails_decorators/base'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RailsDecorators
|
2
|
+
class Base
|
3
|
+
include ActionView::Helpers::TagHelper
|
4
|
+
include ActionView::Helpers::UrlHelper
|
5
|
+
include ActionView::Helpers::TextHelper
|
6
|
+
|
7
|
+
def initialize(subject)
|
8
|
+
subject.public_methods.each do |method|
|
9
|
+
(class << self; self; end).class_eval do
|
10
|
+
define_method method do |*args|
|
11
|
+
subject.send method, *args
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: rails_decorators
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jeff Casimir
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-06-01 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: rspec-rails
|
@@ -34,14 +33,14 @@ extra_rdoc_files: []
|
|
34
33
|
|
35
34
|
files:
|
36
35
|
- lib/generators/decorator/model/model_generator.rb
|
36
|
+
- lib/generators/decorator/model/templates/model.rb
|
37
37
|
- lib/generators/decorator/model/templates/module.rb
|
38
38
|
- lib/generators/decorator/model/USAGE
|
39
39
|
- lib/generators/decorator/setup/setup_generator.rb
|
40
40
|
- lib/generators/decorator/setup/USAGE
|
41
|
-
- lib/rails_decorators/
|
41
|
+
- lib/rails_decorators/base.rb
|
42
42
|
- lib/rails_decorators.rb
|
43
43
|
- README.markdown
|
44
|
-
has_rdoc: true
|
45
44
|
homepage: http://github.com/jcasimir/rails_decorators
|
46
45
|
licenses: []
|
47
46
|
|
@@ -65,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
64
|
requirements: []
|
66
65
|
|
67
66
|
rubyforge_project: rails_decorators
|
68
|
-
rubygems_version: 1.
|
67
|
+
rubygems_version: 1.7.2
|
69
68
|
signing_key:
|
70
69
|
specification_version: 3
|
71
70
|
summary: Decorator pattern implmentation for Rails.
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module RailsDecorators
|
2
|
-
class Loader
|
3
|
-
def self.load
|
4
|
-
ActionController::Dispatcher.to_prepare do
|
5
|
-
models = Dir.glob(File.join(Rails.root, '/app/models/*.rb')).map do |file|
|
6
|
-
file.match(/(\w*).rb/)[1].split('_').map do |class_part|
|
7
|
-
class_part.capitalize
|
8
|
-
end.join('')
|
9
|
-
end
|
10
|
-
|
11
|
-
models.each do |model|
|
12
|
-
begin
|
13
|
-
decorator = (model + "Decorator").constantize
|
14
|
-
model.constantize.send :include, decorator
|
15
|
-
rescue NameError
|
16
|
-
# No Decorator Exists
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|