bird_on_it 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +180 -0
  3. data/lib/bird_on_it.rb +1 -2
  4. data/lib/bird_on_it/decorator.rb +2 -0
  5. data/lib/bird_on_it/version.rb +1 -1
  6. data/test/bird_on_it_test.rb +28 -24
  7. data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
  8. data/test/dummy/app/controllers/tote_bags_controller.rb +62 -0
  9. data/test/dummy/app/decorators/tote_bag_decorator.rb +23 -0
  10. data/test/dummy/app/models/tote_bag.rb +5 -0
  11. data/test/dummy/app/views/tote_bags/_form.html.erb +29 -0
  12. data/test/dummy/app/views/tote_bags/edit.html.erb +6 -0
  13. data/test/dummy/app/views/tote_bags/index.html.erb +31 -0
  14. data/test/dummy/app/views/tote_bags/new.html.erb +5 -0
  15. data/test/dummy/app/views/tote_bags/show.html.erb +20 -0
  16. data/test/dummy/config/routes.rb +2 -0
  17. data/test/dummy/db/development.sqlite3 +0 -0
  18. data/test/dummy/db/migrate/20140314082207_create_tote_bags.rb +11 -0
  19. data/test/dummy/db/schema.rb +24 -0
  20. data/test/dummy/db/test.sqlite3 +0 -0
  21. data/test/dummy/log/development.log +2477 -0
  22. data/test/dummy/log/test.log +41 -0
  23. data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  24. data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  25. data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  26. data/test/dummy/tmp/cache/assets/development/sprockets/371bf96e99717688ed7313a0c53f4212 +0 -0
  27. data/test/dummy/tmp/cache/assets/development/sprockets/510da110ae528e2d22533be39ff696c5 +0 -0
  28. data/test/dummy/tmp/cache/assets/development/sprockets/580936cd6180b0b516e323ab3b9290bc +0 -0
  29. data/test/dummy/tmp/cache/assets/development/sprockets/6fc757c2c8329244ca95d6909865bbc2 +0 -0
  30. data/test/dummy/tmp/cache/assets/development/sprockets/a77bf3dfc15bd59697d7d51fa6a8ebaa +0 -0
  31. data/test/dummy/tmp/cache/assets/development/sprockets/ab3c84b0c7e5ccef7125dd305453cb65 +0 -0
  32. data/test/dummy/tmp/cache/assets/development/sprockets/c530934da9c9116acfdee38204d3cf73 +0 -0
  33. data/test/dummy/tmp/cache/assets/development/sprockets/ce526a9f65d6aa96f21611970cabe56c +0 -0
  34. data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  35. data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  36. data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  37. data/test/dummy/tmp/cache/assets/development/sprockets/fa809407dd24d48afa01dc34b11c60b6 +0 -0
  38. metadata +57 -7
  39. data/README.rdoc +0 -3
  40. data/test/dummy/app/decorators/canvas_bag_decorator.rb +0 -7
  41. data/test/dummy/app/models/canvas_bag.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b1143de097aadc7beb49c385173ea1abc1cf75e4
4
- data.tar.gz: a41af0dd48da71e3377abc6284d896cd7ea493b3
3
+ metadata.gz: 52177a68952ac06a60c8fcfebd8b6b93b5d3bc14
4
+ data.tar.gz: 5c5b3cc9e324e96b571dc5096e24c3f57dfb443e
5
5
  SHA512:
6
- metadata.gz: 135f83b1db2d868ddcf240a4efbf2d32fe8b6a404a3b2afd21a522576086a20b255577396eb5e45f0b4f8133a0e5e378aa0dfe92dd64b411d3dfef3ca7b6687b
7
- data.tar.gz: c4b414dd898e951e5b798e7685f47efbe49f3ba451404c1ba5598072b2dfafe94e029b46c9eaa46cd4aaf450def059798db4474e637617b1f65ab18c029d5a5c
6
+ metadata.gz: 3cf19d242f8313349af3ae73ad56fe4ec8be6f3dccef05ce885808b4e7c54f303f3d9572bc29d9222e93aebc75c03ff9e88604dfd3d10d248b6f658c9d91b17b
7
+ data.tar.gz: be11ec856d3b5d96e7df336733fe4ecf35b782512ec5ae9887c1303b2ee886c4b4a83c1556f6138af670f8429d2355137a439ce6b433ac763b4e4238f443e81b
data/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Bird On It: Simple decorators for your Rails models
2
+
3
+ Model need decorating in your Rails view? Put a bird on it!
4
+
5
+ ## What is Bird On It?
6
+
7
+ _Bird On It_ is a simple way to decorate models in your Rails application.
8
+
9
+ Decorators allow you to move view- and presentation-related logic out of your active record models and view helpers and into decorator classes. Doing this cleans up your view code and reduces the number of responsibilities your active record objects have.
10
+
11
+ Here's how it works. Let's say you have a ToteBag model, with colour, size and straps attributes. In your view, you want to describe whether or not the bag has straps. You also want to add some css classes, depending on what properties the bag has. You put the first of these methods on the model, and the second in a helper, like so:
12
+
13
+ ```ruby
14
+ # app/models/tote_bag.rb
15
+ class ToteBag < ActiveRecord::Base
16
+ def straps_description
17
+ if straps?
18
+ 'This bag has straps'
19
+ else
20
+ 'This bag does not have straps'
21
+ end
22
+ end
23
+ end
24
+ ```
25
+
26
+ ```ruby
27
+ # app/helpers/tote_bags_helper.rb
28
+ module ToteBagHelper
29
+ def tote_bag_css_classes(tote_bag)
30
+ tote_bag_css_classes = %w{tote-bag}
31
+ tote_bag_css_classes << 'straps' if tote_bag.straps?
32
+ tote_bag_css_classes.join(' ')
33
+ end
34
+ end
35
+ ```
36
+
37
+ In your view, you would use both the helper and model:
38
+
39
+ ```erb
40
+ <div class="<%= tote_bag_css_classes(@tote_bag) %>">
41
+ <h2>Straps:</h2>
42
+ <p><%= @tote_bag.straps_description %></p>
43
+ </div>
44
+ ```
45
+
46
+ Instead of the above, however, using a tote bag decorator allows both these methods to exist in the one place. The decorator sends any method that it doesn't itself respond to the decorated object. Using _Bird On It_:
47
+
48
+ ```ruby
49
+ # app/models/tote_bag.rb
50
+ class ToteBag < ActiveRecord::Base
51
+ include BirdOnIt
52
+ end
53
+ ```
54
+
55
+ ```ruby
56
+ # app/decorators/tote_bag_decorator.rb
57
+ class ToteBagDecorator
58
+ include BirdOnIt::Decorator
59
+
60
+ def straps_description
61
+ if object.straps?
62
+ 'This bag has straps'
63
+ else
64
+ 'This bag does not have straps'
65
+ end
66
+ end
67
+
68
+ def css_classes
69
+ css_classes = %w{tote-bag}
70
+ css_classes << 'straps' if object.straps?
71
+ css_classes.join(' ')
72
+ end
73
+ end
74
+ ```
75
+
76
+ In your view:
77
+
78
+ ```erb
79
+ <div class="<%= @tote_bag.css_classes %>">
80
+ <h2>Straps:</h2>
81
+ <p><%= @tote_bag.straps_description %></p>
82
+ </div>
83
+ ```
84
+
85
+ Now your view-related code can reside in the decorator, eliminating the need for helper methods and giving your tote bag class one less responsibility.
86
+
87
+ ## Installation
88
+
89
+ Add _Bird On It_ to your Gemfile
90
+
91
+ ```ruby
92
+ gem 'bird_on_it'
93
+ ```
94
+
95
+ Run `bundle install`.
96
+
97
+ ## Usage
98
+
99
+ ### Model
100
+
101
+ To decorate a model, first include _Bird On It_:
102
+
103
+ ```ruby
104
+ # app/models/post.rb
105
+ class Post < ActiveRecord::Base
106
+ include BirdOnIt
107
+ end
108
+ ```
109
+
110
+ Then create a decorator either in `app/views/decorators` or `app/models` with the name matching your model and include `BirdOnIt::Decorator`.
111
+
112
+ ```ruby
113
+ # app/decorators/post_decorator.rb
114
+ class PostDecorator < ActiveRecord::Base
115
+ include BirdOnIt::Decorator
116
+
117
+ def display_title
118
+ object.title.humanize
119
+ end
120
+ end
121
+ ```
122
+
123
+ In the decorator, your decorated object is available via the `object` variable. If you prefer, you can also access it simply by calling methods to which your decorator does not respond:
124
+
125
+ ```ruby
126
+ def display_title
127
+ title.humanize
128
+ end
129
+ ```
130
+
131
+ ### Controller
132
+
133
+ _Bird On It_ adds a `decorate` method you can call on your objects in your controller before they are passed to the view.
134
+
135
+ ```ruby
136
+ # app/controllers/posts_controller.rb
137
+ def show
138
+ @post = Post.find(params[:id]).decorate
139
+ end
140
+ ```
141
+ There is also a `decorate_collection` class method for decorating collections.
142
+
143
+ ```ruby
144
+ # app/controllers/posts_controller.rb
145
+ def index
146
+ @posts = Post.decorate_collection(Post.all)
147
+ end
148
+ ```
149
+
150
+ ### View
151
+
152
+ Use your decorated objects in the view as usual.
153
+
154
+ ```erb
155
+ <div class="post">
156
+ <h1><%= @post.display_title %></h2>
157
+ <p><%= @post.body %></p>
158
+ </div>
159
+ ```
160
+
161
+ Some Rails helpers, such as the edit_path helper, do not work well with decorated objects. If you encounter one of these, you can work around this by passing the helper the decorated object, instead of the decorator. For example:
162
+
163
+ ```ruby
164
+ <%= link_to 'Edit', edit_post_path(@post.object) %>
165
+ ```
166
+
167
+ Other features, such as linking to the show page, or using the object with `form_for`, should continue to work as usual.
168
+
169
+ ```ruby
170
+ <%= link_to 'Show', @post %>
171
+ ```
172
+
173
+ ```ruby
174
+ <%= form_for(@post) do |f| %>
175
+ <%= f.text_field :material %>
176
+ <%= f.submit %>
177
+ <% end %>
178
+ ```
179
+
180
+ This project uses MIT-LICENSE.
data/lib/bird_on_it.rb CHANGED
@@ -11,8 +11,7 @@ module BirdOnIt
11
11
 
12
12
  module ClassMethods
13
13
  def decorate_collection(collection)
14
- collection = collection.to_a
15
- collection.map { |item| decorator_class.new(item) }
14
+ Array(collection).map { |item| decorator_class.new(item) }
16
15
  end
17
16
 
18
17
  def decorator_class
@@ -3,6 +3,8 @@ module BirdOnIt
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
+ extend ActiveModel::Naming
7
+
6
8
  attr_accessor :object
7
9
 
8
10
  def initialize(object)
@@ -1,3 +1,3 @@
1
1
  module BirdOnIt
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,56 +1,60 @@
1
1
  require 'test_helper'
2
2
 
3
3
  describe BirdOnIt do
4
- class Decoratable
4
+ class ToteBag
5
5
  include BirdOnIt
6
6
 
7
- def decoratable_instance_method
8
- 'hello from decoratable'
7
+ def color
8
+ 'red'
9
9
  end
10
10
  end
11
11
 
12
- class DecoratableDecorator
12
+ class ToteBagDecorator
13
13
  include BirdOnIt::Decorator
14
14
 
15
- def decorator_method
16
- 'hello from decorator'
15
+ def color_description
16
+ if object.color
17
+ "This bag is #{color}"
18
+ else
19
+ "Unknown color"
20
+ end
17
21
  end
18
22
  end
19
23
 
20
- let(:decoratable) { Decoratable.new }
21
- let(:decorated) { decoratable.decorate }
22
-
23
- describe "when included" do
24
- it "adds a class method #decorator_class that returns the decorator class" do
25
- Decoratable.decorator_class.must_equal DecoratableDecorator
26
- end
24
+ let(:tote_bag) { ToteBag.new }
25
+ let(:tote_bag_decorator) { tote_bag.decorate }
27
26
 
27
+ describe "when BirdOnIt is included" do
28
28
  it "adds a method #decorate that returns a decorator" do
29
- decoratable.decorate.class.must_equal DecoratableDecorator
29
+ tote_bag.decorate.class.must_equal ToteBagDecorator
30
30
  end
31
31
 
32
32
  it "adds a class method #decorate_collection that decorates a collection" do
33
- collection = Decoratable.decorate_collection [Decoratable.new, Decoratable.new]
34
- collection.map(&:class).must_equal [DecoratableDecorator, DecoratableDecorator]
33
+ collection = ToteBag.decorate_collection [ToteBag.new, ToteBag.new]
34
+ collection.map(&:class).must_equal [ToteBagDecorator, ToteBagDecorator]
35
+ end
36
+
37
+ it "adds a class method #decorator_class that returns the decorator class" do
38
+ ToteBag.decorator_class.must_equal ToteBagDecorator
35
39
  end
36
40
  end
37
41
 
38
- describe DecoratableDecorator do
42
+ describe ToteBagDecorator do
39
43
  it "responds to its own instance methods" do
40
- decorated.respond_to?(:decorator_method).must_equal true
41
- decorated.decorator_method.must_equal 'hello from decorator'
44
+ tote_bag_decorator.respond_to?(:color_description).must_equal true
45
+ tote_bag_decorator.color_description.must_equal 'This bag is red'
42
46
  end
43
47
 
44
- it "responds to the methods of the decorated object" do
45
- decorated.respond_to?(:decoratable_instance_method).must_equal true
48
+ it "sends missing methods to its decorated object" do
49
+ tote_bag_decorator.color.must_equal 'red'
46
50
  end
47
51
 
48
- it "sends missing methods to its decorated object" do
49
- decorated.decoratable_instance_method.must_equal 'hello from decoratable'
52
+ it "responds to the methods of the decorated object" do
53
+ tote_bag_decorator.respond_to?(:color).must_equal true
50
54
  end
51
55
 
52
56
  it "has an object attribute that is the decorated object" do
53
- decorated.object.must_equal decoratable
57
+ tote_bag_decorator.object.must_equal tote_bag
54
58
  end
55
59
  end
56
60
  end
@@ -0,0 +1,56 @@
1
+ body { background-color: #fff; color: #333; }
2
+
3
+ body, p, ol, ul, td {
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px;
7
+ }
8
+
9
+ pre {
10
+ background-color: #eee;
11
+ padding: 10px;
12
+ font-size: 11px;
13
+ }
14
+
15
+ a { color: #000; }
16
+ a:visited { color: #666; }
17
+ a:hover { color: #fff; background-color:#000; }
18
+
19
+ div.field, div.actions {
20
+ margin-bottom: 10px;
21
+ }
22
+
23
+ #notice {
24
+ color: green;
25
+ }
26
+
27
+ .field_with_errors {
28
+ padding: 2px;
29
+ background-color: red;
30
+ display: table;
31
+ }
32
+
33
+ #error_explanation {
34
+ width: 450px;
35
+ border: 2px solid red;
36
+ padding: 7px;
37
+ padding-bottom: 0;
38
+ margin-bottom: 20px;
39
+ background-color: #f0f0f0;
40
+ }
41
+
42
+ #error_explanation h2 {
43
+ text-align: left;
44
+ font-weight: bold;
45
+ padding: 5px 5px 5px 15px;
46
+ font-size: 12px;
47
+ margin: -7px;
48
+ margin-bottom: 0px;
49
+ background-color: #c00;
50
+ color: #fff;
51
+ }
52
+
53
+ #error_explanation ul li {
54
+ font-size: 12px;
55
+ list-style: square;
56
+ }
@@ -0,0 +1,62 @@
1
+ class ToteBagsController < ApplicationController
2
+ respond_to :html
3
+
4
+ # GET /tote_bags
5
+ def index
6
+ @tote_bags = ToteBag.decorate_collection(ToteBag.all)
7
+ respond_with @tote_bags
8
+ end
9
+
10
+ # GET /tote_bags/1
11
+ def show
12
+ @tote_bag = ToteBag.find(params[:id]).decorate
13
+ respond_with @tote_bag
14
+ end
15
+
16
+ # GET /tote_bags/new
17
+ def new
18
+ @tote_bag = ToteBag.new
19
+ respond_with @tote_bag
20
+ end
21
+
22
+ # GET /tote_bags/1/edit
23
+ def edit
24
+ @tote_bag = ToteBag.find(params[:id]).decorate
25
+ respond_with @tote_bag
26
+ end
27
+
28
+ # POST /tote_bags
29
+ def create
30
+ @tote_bag = ToteBag.new(tote_bag_params)
31
+
32
+ if @tote_bag.save
33
+ redirect_to @tote_bag, notice: 'Tote bag was successfully created.'
34
+ else
35
+ render :new
36
+ end
37
+ end
38
+
39
+ # PATCH/PUT /tote_bags/1
40
+ def update
41
+ @tote_bag = ToteBag.find(params[:id])
42
+
43
+ if @tote_bag.update(tote_bag_params)
44
+ redirect_to @tote_bag, notice: 'Tote bag was successfully updated.'
45
+ else
46
+ render :edit
47
+ end
48
+ end
49
+
50
+ # DELETE /tote_bags/1
51
+ def destroy
52
+ @tote_bag = ToteBag.find(params[:id])
53
+ @tote_bag.destroy
54
+ redirect_to tote_bags_url, notice: 'Tote bag was successfully destroyed.'
55
+ end
56
+
57
+ private
58
+
59
+ def tote_bag_params
60
+ params.require(:tote_bag).permit(:colour, :material, :straps)
61
+ end
62
+ end
@@ -0,0 +1,23 @@
1
+ class ToteBagDecorator
2
+ include BirdOnIt::Decorator
3
+
4
+ def css_classes
5
+ css_classes = %w{tote-bag}
6
+
7
+ css_classes << 'straps' if object.straps?
8
+
9
+ css_classes.join(' ')
10
+ end
11
+
12
+ def colour_description
13
+ object.colour.present? ? object.colour : 'Has no colour'
14
+ end
15
+
16
+ def material_description
17
+ object.material.present? ? object.material : 'Unknown material'
18
+ end
19
+
20
+ def straps_description
21
+ object.straps? ? 'Has straps' : 'Strapless'
22
+ end
23
+ end