flowmor_router 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +212 -38
- data/Rakefile +0 -2
- data/config/routes.rb +12 -18
- data/lib/flowmor_router/acts_as_routable.rb +56 -0
- data/lib/flowmor_router/exceptions.rb +1 -0
- data/lib/flowmor_router/foo.rb +63 -0
- data/lib/flowmor_router/proxy_foo.rb +50 -0
- data/lib/flowmor_router/router_classes.rb +261 -0
- data/lib/flowmor_router/version.rb +1 -1
- data/lib/flowmor_router.rb +2 -1
- data/test/dummy/app/models/article.rb +7 -1
- data/test/dummy/app/models/news_article.rb +1 -1
- data/test/dummy/app/models/post.rb +12 -11
- data/test/dummy/app/models/post_category.rb +2 -2
- data/test/dummy/log/test.log +86734 -0
- data/test/dummy/test/integration/routable_records_test.rb +2 -1
- data/test/dummy/test/models/article_test.rb +16 -15
- data/test/dummy/test/models/news_article_test.rb +11 -24
- data/test/dummy/test/models/post_category_test.rb +4 -29
- data/test/dummy/test/models/post_test.rb +10 -40
- data/test/router_classes_test.rb +219 -0
- metadata +8 -3
- data/lib/flowmor_router/acts_as_flowmor_routable.rb +0 -122
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b98386121b4bdf28961d5609e38be1b73e05cc47
|
4
|
+
data.tar.gz: 603ce477d9752cd29bd925367d289a1d3d40de76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b61723cb2b8612f01af3f75d56240544aa7487af10f648b12866400ff60a962a6713131b5f1861d0a6fa852be4b9e884ceb1b651b9e7b04e115d88464dc0c439
|
7
|
+
data.tar.gz: 6e3b88f55e1afbdd752ea9465c507fa9c42f727e245e882d23255e21868bc52dc922f11feb09ee11fdb266db60bcfd4ab2e6e0b0fbeeea0eb89d77163ffc5eaf
|
data/README.md
CHANGED
@@ -8,7 +8,6 @@ class Post < ActiveRecord::Base
|
|
8
8
|
end
|
9
9
|
|
10
10
|
p = Post.create(title: "My First Post")
|
11
|
-
puts p.name # => "my-first-example"
|
12
11
|
puts p.path # => "/posts/my-first-example"
|
13
12
|
```
|
14
13
|
|
@@ -22,7 +21,7 @@ or in a view where PostCategory has_many :posts
|
|
22
21
|
%li= link_to post.title, post.path
|
23
22
|
```
|
24
23
|
|
25
|
-
FlowmorRouter also supports static pages. All you have to do is create an app/views/static folder and place templates in that folder. Routes are automatically generated and served by the StaticController. Currently, this only goes one level deep. That is, sub-directories of static not yet implemented.
|
24
|
+
FlowmorRouter also supports static pages. All you have to do is create an app/views/static folder and place templates in that folder. Routes are automatically generated and served by the StaticController. Currently, this only goes one level deep. That is, sub-directories of static not yet implemented. Useful for rapidly importing WordPress archive pages! For an idea of how I rapidly ported my WordPress and python/Zope websites to Rails, check out the following [gist](https://gist.github.com/mwlang/a4dbf5c098fd8f9502b2).
|
26
25
|
|
27
26
|
If you're blogging with markdown or other file-based approaches, you'll appreciate how easy it is to reference your posts in those static views with linking:
|
28
27
|
|
@@ -31,21 +30,160 @@ If you're blogging with markdown or other file-based approaches, you'll apprecia
|
|
31
30
|
%p Please be sure to read my post, #{link_to "How to Use Flowmor Router", post_how_to_use_flowmor_router_path}
|
32
31
|
```
|
33
32
|
|
34
|
-
Every model instance's
|
33
|
+
Every model instance's path is named after the model and title/slug/name for the record. How the paths and path names are generated can be customized.
|
34
|
+
|
35
35
|
|
36
36
|
## State of the project
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
* 0.2.1
|
39
|
+
* Tested and Works on Rails 4.x and Ruby 2.x
|
40
|
+
* Changed usage pattern to use act_as_routable instead of inheriting from RoutableRecord
|
41
|
+
* Completely refactored to remove as much code out of the ActiveRecord class and into a new RouterClasses object.
|
42
|
+
* Also greatly simplified some of the implementation and cleaned up weird naming conventions.
|
43
|
+
* Added ability to have multiple actors on one model
|
44
|
+
* Added suffix and prefix
|
41
45
|
|
42
46
|
Its got enough functionality to work really well for me [(mwlang)](https://github.com/mwlang) in its current form. It's a simple implementation with relatively few lines of code, adequately test covered. It works and is used in production on a handful of sites. You can see it in action on [my personal site](http://codeconnoisseur.org) and [business site](http://cybrains.net).
|
43
47
|
|
44
48
|
### Is it For You?
|
45
49
|
|
46
|
-
This isn't for everyone.
|
50
|
+
This isn't for everyone. The Flowmor Router build routes ahead of time based on objects in the database. Rails purists will argue this method pollutes the routes space. It does provide functionality similar to [friendly_id](https://github.com/norman/friendly_id) or by simply redefining the id of a model with AR's #to_param. If you run multiple instances of an application, you'll need to take care of syncing when the database is updated. The simplest way to do this is by adding Post.reload_routes (for example) to the before_filter callback of the controller. Sounds like a performance killer, but its really not. Just think every time you refresh during development that the routes are reloaded!
|
51
|
+
|
52
|
+
On the other hand, this approach allows you a lot of flexibility to creating truly custom routes in your app. It also allows you to avoid using a global "match any" in your config/routes.rb. A use case is porting over a WordPress site to Rails where there was a highly customized permalink structure in place. It's really only meant for "#show" actions. I personally wouldn't try to also incorporate CRUD actions with friendly route names. Rails' conventional routes does the job extremely well for CRUD actions. This also means other gems like ActiveAdmin will work as advertised since friendly routes aren't interfering with Rails routes.
|
53
|
+
|
54
|
+
## TL;DR
|
55
|
+
|
56
|
+
For those of you who just need good examples and not a lot of words. The following examples are class definitions followed by what's generated:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class KitchenSink < ActiveRecord::Base
|
60
|
+
acts_as_routable :sink,
|
61
|
+
scope: -> { where(nothing_missing: true) }
|
62
|
+
prefix: -> { :kitchen },
|
63
|
+
suffix: [:faucet, :drain],
|
64
|
+
delimiter: "_",
|
65
|
+
name_field: :appliance
|
66
|
+
title_field: :display
|
67
|
+
name: -> { :sluggerize }
|
68
|
+
route: -> { :route }
|
69
|
+
|
70
|
+
def kitchen
|
71
|
+
[:fancy, :kitchen]
|
72
|
+
end
|
73
|
+
|
74
|
+
def sluggerize
|
75
|
+
self.title.downcase.parameterize("_")
|
76
|
+
end
|
77
|
+
|
78
|
+
def route
|
79
|
+
"/kitchen/sink/#{sluggerize}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
NOTE: prefix and suffix can be either a Symbol/String, an Array of such, or a Proc which references a method on the Model that returns a String/Symbol or Array of such.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class Post < ActiveRecord::Base
|
87
|
+
acts_as_routable
|
88
|
+
end
|
89
|
+
|
90
|
+
@post = Post.create(title: "Title me Silly")
|
91
|
+
|
92
|
+
route_name = posts_title_me_silly
|
93
|
+
route_path = posts_title_me_silly_path
|
94
|
+
route_url = posts_title_me_silly_url
|
95
|
+
@post.path = /posts/title-me-silly
|
96
|
+
@post.url = http://example.com/posts/title-me-silly
|
97
|
+
```
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
class Post < ActiveRecord::Base
|
101
|
+
acts_as_routable :ramblings
|
102
|
+
end
|
103
|
+
|
104
|
+
@post = Post.create(title: "Title me Silly")
|
105
|
+
|
106
|
+
route_name = ramblings_title_me_silly
|
107
|
+
route_path = ramblings_title_me_silly_path
|
108
|
+
route_url = ramblings_title_me_silly_url
|
109
|
+
@post.path = /ramblings/title-me-silly
|
110
|
+
@post.url = http://example.com/ramblings/title-me-silly
|
111
|
+
```
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
class Post < ActiveRecord::Base
|
115
|
+
acts_as_routable :ramblings, prefix: :posts
|
116
|
+
end
|
117
|
+
|
118
|
+
@post = Post.create(title: "Title me Silly")
|
119
|
+
|
120
|
+
route_name = posts_ramblings_title_me_silly
|
121
|
+
route_path = posts_ramblings_title_me_silly_path
|
122
|
+
route_url = posts_ramblings_title_me_silly_url
|
123
|
+
@post.path = /posts/ramblings/title-me-silly
|
124
|
+
@post.url = http://example.com/posts/ramblings/title-me-silly
|
125
|
+
```
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class Post < ActiveRecord::Base
|
129
|
+
acts_as_routable :ramblings, prefix: [:blog, :posts]
|
130
|
+
end
|
131
|
+
|
132
|
+
@post = Post.create(title: "Title me Silly")
|
133
|
+
|
134
|
+
route_name = blog_posts_ramblings_title_me_silly
|
135
|
+
route_path = blog_posts_ramblings_title_me_silly_path
|
136
|
+
route_url = blog_posts_ramblings_title_me_silly_url
|
137
|
+
@post.path = /blog/posts/ramblings/title-me-silly
|
138
|
+
@post.url = http://example.com/blog/posts/ramblings/title-me-silly
|
139
|
+
```
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
class Post < ActiveRecord::Base
|
143
|
+
belongs_to :category
|
144
|
+
acts_as_routable :ramblings, prefix: -> { category.name }
|
145
|
+
acts_as_routable :archive, suffix: [:posts]
|
146
|
+
end
|
147
|
+
|
148
|
+
@post = Post.create(title: "Title me Silly")
|
149
|
+
|
150
|
+
route_name = silly_category_ramblings_title_me_silly
|
151
|
+
route_path = silly_category_ramblings_title_me_silly_path
|
152
|
+
route_url = silly_category_ramblings_title_me_silly_url
|
153
|
+
@post.path = /silly-category/posts/ramblings/title-me-silly
|
154
|
+
@post.url = http://example.com/silly-category/posts/ramblings/title-me-silly
|
155
|
+
|
156
|
+
# AND
|
157
|
+
|
158
|
+
route_name = archive_posts_title_me_silly
|
159
|
+
route_path = archive_posts_title_me_silly_path
|
160
|
+
route_url = archive_posts_title_me_silly_url
|
161
|
+
@post.posts_archive_path = /archive/posts/title-me-silly
|
162
|
+
@post.posts_archive_url = http://example.com/archive/posts/title-me-silly
|
163
|
+
```
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
class Post < ActiveRecord::Base
|
167
|
+
acts_as_routable, scope: -> { where(published: true) }
|
168
|
+
acts_as_routable :archive, prefix: [:posts]
|
169
|
+
end
|
170
|
+
|
171
|
+
@post = Post.create(title: "Title me Silly")
|
172
|
+
|
173
|
+
route_name = posts_title_me_silly
|
174
|
+
route_path = posts_title_me_silly_path
|
175
|
+
route_url = posts_title_me_silly_url
|
176
|
+
@post.path = /posts/title-me-silly
|
177
|
+
@post.url = http://example.com/posts/title-me-silly
|
47
178
|
|
48
|
-
|
179
|
+
# AND
|
180
|
+
|
181
|
+
route_name = posts_archive_title_me_silly
|
182
|
+
route_path = posts_archive_title_me_silly_path
|
183
|
+
route_url = posts_archive_title_me_silly_url
|
184
|
+
@post.posts_archive_path = /posts/archive/title-me-silly
|
185
|
+
@post.posts_archive_url = http://example.com/posts/archive/title-me-silly
|
186
|
+
```
|
49
187
|
|
50
188
|
### To Install
|
51
189
|
|
@@ -59,7 +197,7 @@ And then run the `bundle install` command.
|
|
59
197
|
|
60
198
|
## Convention over Configuration
|
61
199
|
|
62
|
-
I wanted a *simple* implementation and library to work with, so the convention is the model has a `title` field and a `name` field. You (or your user) sets the title and the name field
|
200
|
+
I wanted a *simple* implementation and library to work with, so the convention is the model has a `title` field and a `name` field. You (or your user) sets the title and the name field, which should be populated with a parameterized/routable value. Just for kicks, if you don't have a name field on the model, then the Title field is always used to generate a parameterized value. Hyphens are used by default instead of underscores because Google Webmaster Guidelines favors hyphens over underscores for SEO. But you can override this passing the :delimiter option.
|
63
201
|
|
64
202
|
For example, "FlowmorRouter, the amazing little engine that could" will populate the name field with 'flowmor-router-the-amazing-little-engine-that-could'. The controller by convention will have the same name as the model's name while the default action will be the #show action on that controller. If you have a Post model, then its expected that your application has a PostController. You're expected to provide the controller implementation. Here's an example:
|
65
203
|
|
@@ -86,17 +224,45 @@ class Post < ActiveRecord::Base
|
|
86
224
|
end
|
87
225
|
```
|
88
226
|
|
227
|
+
### By Convention...
|
228
|
+
|
229
|
+
The router looks for a :name field as the valid end of the route string ("my-fancy-post-title"). You're responsible for populating this field with a valid and sensible value for URI strings.
|
230
|
+
|
231
|
+
If the :name field is missing, the router will compute a parameterized name from the :title field. That is, if the :title field contains "My Fancy Pants Post" then the computed name (a.k.a. slug, or parameterized value) will be "my-fancy-pants-post". This is appended to arrive at the fully qualified route.
|
232
|
+
|
233
|
+
The model that you add "acts_as_routable" to becomes the root of the route. So Post ```ActiveRecord::Base; acts_as_routable; end``` will yield routes starting at "/posts/" and ultimately "/posts/my-fancy-pants-posts" in the above example's case.
|
234
|
+
|
89
235
|
### Conventions Suck, I Really Want to Customize!
|
90
236
|
|
91
|
-
Ok, here's how to do it.
|
237
|
+
Ok, here's how to do it.
|
238
|
+
|
239
|
+
|
240
|
+
#### :title_field, :name_field and :name
|
241
|
+
|
242
|
+
To change the field that the route name is derived from:
|
92
243
|
|
93
244
|
```ruby
|
94
245
|
class NewsArticle < ActiveRecord::Base
|
95
246
|
acts_as_routable \
|
96
|
-
|
97
|
-
name_field: :slug
|
247
|
+
title_field: :caption, # changes from :title to :caption
|
248
|
+
name_field: :slug # changes from :name to :slug
|
98
249
|
end
|
99
250
|
```
|
251
|
+
|
252
|
+
Alternatively, you can do a lazy evaluation that incorporates other data for the record by passing the :name property
|
253
|
+
|
254
|
+
```ruby
|
255
|
+
class NewsArticle < ActiveRecord::Base
|
256
|
+
acts_as_routable name: :custom_slug
|
257
|
+
|
258
|
+
def custom_slug
|
259
|
+
"#{self.author.name}_#{self.name}".parameterize("_")
|
260
|
+
end
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
264
|
+
Using the :name property supersedes both :name_field and :title_field properties
|
265
|
+
|
100
266
|
To change the controller and action:
|
101
267
|
|
102
268
|
```ruby
|
@@ -108,44 +274,52 @@ end
|
|
108
274
|
To change how the route and route name are constructed (say you have Post that belongs_to PostCategory and need to avoid naming collision should two posts have same title, but belong to different categories):
|
109
275
|
|
110
276
|
```ruby
|
111
|
-
class
|
112
|
-
|
277
|
+
class Post < ActiveRecord::Base
|
278
|
+
belongs_to :category
|
113
279
|
|
114
|
-
acts_as_routable
|
280
|
+
acts_as_routable prefix: -> { :category },
|
281
|
+
controller_action: "blog#show"
|
115
282
|
|
116
|
-
#
|
117
|
-
|
118
|
-
|
119
|
-
# route name becomes "category_general" instead of "post_category_general"
|
120
|
-
def route_model
|
121
|
-
"category"
|
283
|
+
# defaults to "general" category when none assigned
|
284
|
+
def category
|
285
|
+
(self.category.try(:name) || "general").parameterize
|
122
286
|
end
|
123
287
|
end
|
124
288
|
|
125
|
-
|
126
|
-
|
289
|
+
@post.create('Programming Ruby', category: Category.find_by_name("General"))
|
290
|
+
@post.path # => /general/posts/programming-ruby
|
291
|
+
```
|
127
292
|
|
128
|
-
|
293
|
+
Note that the Proc triggers calling the model's "category" method when it's time to construct the route name and path.
|
129
294
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
category.try(:name) || 'general'
|
140
|
-
end
|
295
|
+
Similar to :prefix is the :suffix and it's inserted into the route constructed right before the record's name value.
|
296
|
+
|
297
|
+
### route
|
298
|
+
|
299
|
+
If you want to skip all the fancy route building provided by the Engine, then pass in a Proc to the :route option.
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
class Post < ActiveRecord::Base
|
303
|
+
acts_as_routable route: :custom_route
|
141
304
|
|
142
|
-
|
143
|
-
|
144
|
-
"/#{category_name}/#{name}"
|
305
|
+
def custom_route
|
306
|
+
"/posts/#{date_created.strftime("%Y/%m/%d/")}#{name.parameterize}"
|
145
307
|
end
|
146
308
|
end
|
147
309
|
```
|
148
310
|
|
311
|
+
### delimiter
|
312
|
+
|
313
|
+
The :delimiter option allows you to change the default hyphen to something else when the route name is computed from the title field.
|
314
|
+
|
315
|
+
```
|
316
|
+
class Post < ActiveRecord::Base
|
317
|
+
acts_as_routable delimiter: "_-_"
|
318
|
+
end
|
319
|
+
|
320
|
+
@post.path # => /posts/my_-_silly_-_title
|
321
|
+
```
|
322
|
+
|
149
323
|
If you need to get any fancier than that, then just about everything you need can be found in the [lib/flowmor_router/acts_as_flowmor_routable.rb](https://github.com/mwlang/flowmor_router/blob/master/lib/flowmor_router/acts_as_flowmor_routable.rb) implementation.
|
150
324
|
|
151
325
|
By default, all acts_as_routable models and their instances are added to the routes table. What gets routed can be customized by supplying a :scope option.
|
data/Rakefile
CHANGED
data/config/routes.rb
CHANGED
@@ -1,23 +1,6 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
2
|
|
3
|
-
FlowmorRouter::
|
4
|
-
model.routable.each do |record|
|
5
|
-
get record.route,
|
6
|
-
to: record.controller_action,
|
7
|
-
defaults: { id: record.id },
|
8
|
-
as: record.route_name
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# Routes from RoutableRecord descendants
|
13
|
-
RoutableRecord.descendants.each do |model|
|
14
|
-
model.routable.each do |record|
|
15
|
-
get record.route,
|
16
|
-
to: record.controller_action,
|
17
|
-
defaults: { id: record.id },
|
18
|
-
as: record.route_name
|
19
|
-
end
|
20
|
-
end
|
3
|
+
puts "REDRAWING ROUTES for #{FlowmorRouter::RouterClasses.router_classes.map{|m| m.model.name.to_s}}"
|
21
4
|
|
22
5
|
# Routes from app/view/static
|
23
6
|
Dir.glob(File.join(Rails.root, 'app', 'views', 'static', '*')).reject{|r| File.directory?(r)}.each do |fn|
|
@@ -27,4 +10,15 @@ Rails.application.routes.draw do
|
|
27
10
|
get("/#{route.gsub("_", "-")}", to: "static##{route}", as: "static_#{route.gsub("-", "_")}")
|
28
11
|
end
|
29
12
|
end
|
13
|
+
|
14
|
+
FlowmorRouter::RouterClasses.router_classes.each do |router_class|
|
15
|
+
puts " MODEL: #{router_class.model.name}"
|
16
|
+
router_class.routable.each do |record|
|
17
|
+
puts " ROUTING: #{router_class.route_path(record)} to: #{router_class.controller_action} defaults: { id: #{record.id} } as: #{router_class.route_name(record)}"
|
18
|
+
get router_class.route_path(record),
|
19
|
+
to: router_class.controller_action,
|
20
|
+
defaults: { id: record.id },
|
21
|
+
as: router_class.route_name(record)
|
22
|
+
end
|
23
|
+
end
|
30
24
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module FlowmorRouter
|
2
|
+
module ActsAsRoutable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
|
7
|
+
after_save :reload_routes
|
8
|
+
|
9
|
+
def reload_routes
|
10
|
+
Rails.application.routes_reloader.reload!
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
def acts_as_routable(actor = nil, options = {})
|
17
|
+
# juggle what was passed in to correct variables
|
18
|
+
actor, options = nil, actor if actor.is_a?(Hash) && options == {}
|
19
|
+
if actor.nil?
|
20
|
+
singular_name = name.underscore.downcase.split("/").last
|
21
|
+
actor = singular_name.pluralize
|
22
|
+
options[:controller_action] = options[:controller_action] || "#{singular_name}#show"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Register and assign a distinctive name to the router_class
|
26
|
+
router_class = FlowmorRouter::RouterClasses.register(actor, self, options)
|
27
|
+
puts "REGISTERED #{router_class.named_instance}"
|
28
|
+
class_attribute router_class.named_instance
|
29
|
+
self.send "#{router_class.named_instance}=", router_class
|
30
|
+
|
31
|
+
scope router_class.scope_name, options[:scope] || lambda {}
|
32
|
+
|
33
|
+
define_method router_class.route_name_method_name do
|
34
|
+
router_class.route_name self
|
35
|
+
end
|
36
|
+
|
37
|
+
define_method router_class.url_method_name do
|
38
|
+
router_class.url self
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method router_class.path_method_name do
|
42
|
+
router_class.path self
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
Rails.application.routes_reloader.reload! unless Rails.configuration.eager_load
|
47
|
+
rescue SystemStackError
|
48
|
+
# NOP -- Supressing Stack Level Too deep error
|
49
|
+
# caused by models being loaded lazily during development mode.
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
ActiveRecord::Base.send :include, FlowmorRouter::ActsAsRoutable
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Foo
|
2
|
+
def hello
|
3
|
+
puts "gonna say hello"
|
4
|
+
"Hello #{world}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def world
|
8
|
+
"World"
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :say_it
|
12
|
+
|
13
|
+
def initialize options = {}
|
14
|
+
@say_it = options[:another_way] || method(:hello)
|
15
|
+
define_singleton_method(:world, options[:another_world]) if options[:another_world]
|
16
|
+
end
|
17
|
+
|
18
|
+
def speak
|
19
|
+
instance_exec &say_it
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
foobar = Foo.new
|
25
|
+
puts "lazy?"
|
26
|
+
puts foobar.speak
|
27
|
+
rescue Exception => e
|
28
|
+
puts "#{e.class.to_s}: #{e.message}"
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
foobaz = Foo.new(another_way: lambda { "#{hello}, Sir!" })
|
33
|
+
puts "lazy?"
|
34
|
+
puts foobaz.speak
|
35
|
+
rescue Exception => e
|
36
|
+
puts "#{e.class.to_s}: #{e.message}"
|
37
|
+
end
|
38
|
+
|
39
|
+
begin
|
40
|
+
foobaz = Foo.new(another_world: lambda { "Earth" })
|
41
|
+
puts "lazy?"
|
42
|
+
puts foobaz.speak
|
43
|
+
rescue Exception => e
|
44
|
+
puts "#{e.class.to_s}: #{e.message}"
|
45
|
+
end
|
46
|
+
|
47
|
+
class Bar
|
48
|
+
def self.register(name = nil, options = {})
|
49
|
+
if name.nil?
|
50
|
+
name = "foobaz"
|
51
|
+
elsif name.is_a?(Hash) && options == {}
|
52
|
+
name, options = "foobar", name
|
53
|
+
end
|
54
|
+
puts [name, options].inspect
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
Bar.register :something, :foo => 1, :bar => 2
|
59
|
+
Bar.register \
|
60
|
+
foo: 2,
|
61
|
+
bar: -> { puts "yeah" },
|
62
|
+
baz: 3
|
63
|
+
Bar.register
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Foo
|
2
|
+
def hello
|
3
|
+
puts "gonna say hello"
|
4
|
+
"Hello #{world}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def world
|
8
|
+
"World"
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :say_it
|
12
|
+
|
13
|
+
def initialize options = {}
|
14
|
+
@say_it = options[:another_way] || method(:hello)
|
15
|
+
define_singleton_method(:world, options[:another_world]) if options[:another_world]
|
16
|
+
end
|
17
|
+
|
18
|
+
def speak
|
19
|
+
hello
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Proxy
|
24
|
+
def
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
foobar = Foo.new
|
29
|
+
puts "lazy?"
|
30
|
+
puts foobar.speak
|
31
|
+
rescue Exception => e
|
32
|
+
puts "#{e.class.to_s}: #{e.message}"
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
foobaz = Foo.new(another_way: lambda { "#{hello}, Sir!" })
|
37
|
+
puts "lazy?"
|
38
|
+
puts foobaz.speak
|
39
|
+
rescue Exception => e
|
40
|
+
puts "#{e.class.to_s}: #{e.message}"
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
foobaz = Foo.new(another_world: lambda { "Earth" })
|
45
|
+
puts "lazy?"
|
46
|
+
puts foobaz.speak
|
47
|
+
rescue Exception => e
|
48
|
+
puts "#{e.class.to_s}: #{e.message}"
|
49
|
+
end
|
50
|
+
|