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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31be1a7b4a12e22cfe628df2fc2c377b1f3168f8
4
- data.tar.gz: 347abb0a062209b551050aa406fae2cf66ec6c07
3
+ metadata.gz: b98386121b4bdf28961d5609e38be1b73e05cc47
4
+ data.tar.gz: 603ce477d9752cd29bd925367d289a1d3d40de76
5
5
  SHA512:
6
- metadata.gz: 6cb6b02478779500ac77b34003c6b725b368eb116ac643d7143e58cc933ecdbfd0546cb5809ee5dc6f24d6a05e259c5868d12fa2158cf51b9ebd1d4d5129f3a0
7
- data.tar.gz: ea5baea315539090ff19c54fee5fc213d62ace2bc2f43ea5582b43becc43060e5027398125444d6e03887b328977afab6ee017179984fd85eaf6c666095365ef
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 route is named after the model and name.
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
- ### 0.1.1
39
- ### Tested and Works on Rails 4.x and Ruby 2.x
40
- ### added ability to use act_as_routable instead of inheriting from RoutableRecord
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. It does build routes from 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!
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
- 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.
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 gets auto-populated with a routable name. Hyphens are used instead of underscores because Google Webmaster Guidelines favors hyphens over underscores for SEO.
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. To change the field that the route name is derived from:
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
- derived_name_field: :caption, # changes from :title
97
- name_field: :slug # changes from :name
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 PostCategory < ActiveRecord::Base
112
- has_many :posts, foreign_key: "category_id"
277
+ class Post < ActiveRecord::Base
278
+ belongs_to :category
113
279
 
114
- acts_as_routable controller_action: "blog#category"
280
+ acts_as_routable prefix: -> { :category },
281
+ controller_action: "blog#show"
115
282
 
116
- # Not necessary here, but shows you how to change the route's model name.
117
- # Here, we change "post_category" (the default) to "category"
118
- # For example, PostCategory.create(title: "General"), the
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
- class Post < ActiveRecord::Base
126
- belongs_to :category, class_name: "PostCategory", counter_cache: true
289
+ @post.create('Programming Ruby', category: Category.find_by_name("General"))
290
+ @post.path # => /general/posts/programming-ruby
291
+ ```
127
292
 
128
- acts_as_routable controller_action: "blog#show"
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
- # Assuming you have a Post.create(title: "Some Title", category: PostCategory.create(title: "General"))
131
- # The names of the post route's name changes from post_some_title to post_general_some_title by
132
- # appending category name to the route name prefix
133
- def route_name_prefix
134
- super + "_#{category_name}"
135
- end
136
-
137
- # as a bonus, automatically "categorize" as "general" when no category assigned.
138
- def category_name
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
- # The route is also changed in this example from /posts/some-title to /general/some-title
143
- def route
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
@@ -17,8 +17,6 @@ end
17
17
  APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
18
  load 'rails/tasks/engine.rake'
19
19
 
20
-
21
-
22
20
  Bundler::GemHelper.install_tasks
23
21
 
24
22
  require 'rake/testtask'
data/config/routes.rb CHANGED
@@ -1,23 +1,6 @@
1
1
  Rails.application.routes.draw do
2
2
 
3
- FlowmorRouter::ROUTABLE_MODEL_CLASSES.each do |model|
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
@@ -1,4 +1,5 @@
1
1
  module FlowmorRouter
2
2
  class UnroutableRecord < RuntimeError; end
3
3
  class UnroutedRecord < RuntimeError; end
4
+ class DuplicateRouterActors < RuntimeError; end
4
5
  end
@@ -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
+