duck_map 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,209 @@
1
+ require 'active_support/concern'
2
+
3
+ module DuckMap
4
+
5
+ ##################################################################################
6
+ # Mixin module for ActionDispatch::Routing::RouteSet to add support for defining sitemaps directly
7
+ # inside the config/routes.rb of a Rails app.
8
+ module RouteSet
9
+ extend ActiveSupport::Concern
10
+
11
+ ##################################################################################
12
+ # Builds a list of routes associated with a sitemap. The actual list of routes returned is based
13
+ # on {DuckMap::Sitemap::Route::InstanceMethods#sitemap_route_name sitemap_route_name}, which is the named route of the sitemap.
14
+ # @note See {DuckMap::Sitemap::Mapper::InstanceMethods#sitemap} for a full explanation of how to define a sitemap and how those rules affect this method.
15
+ # @param [String] name_or_path The request.path of the current sitemap url or the name assigned to the sitemap via config/routes.rb.
16
+ # @return [Array]
17
+ def find_sitemap_route(name_or_path)
18
+
19
+ name_or_path = name_or_path.to_s
20
+
21
+ # strip off the extension if it exists
22
+ if name_or_path.include?(".")
23
+ name_or_path = name_or_path.slice(0, name_or_path.rindex("."))
24
+ end
25
+
26
+ full_name = "#{name_or_path}_sitemap"
27
+
28
+ # search for a sitemap route matching the path passed to the method.
29
+ # the route MUST be marked as a sitemap. return nothing if the sitemap route cannot be found.
30
+ return self.routes.find {|route| route.is_sitemap? &&
31
+ (route.path.spec.to_s =~ /^#{name_or_path}/ ||
32
+ route.sitemap_route_name.eql?(name_or_path) ||
33
+ route.sitemap_route_name.eql?(full_name))}
34
+ end
35
+
36
+ ##################################################################################
37
+ # Builds a list of routes associated with a sitemap route. The actual list of routes returned is based
38
+ # on {DuckMap::Sitemap::Route::InstanceMethods#sitemap_route_name sitemap_route_name}, which is the named
39
+ # route of the sitemap.
40
+ # @note See {DuckMap::Sitemap::Mapper::InstanceMethods#sitemap} for a full explanation of how to define a sitemap and how those rules affect this method.
41
+ # @param [String] sitemap_route A sitemap route.
42
+ # @return [Array]
43
+ def sitemap_routes(sitemap_route)
44
+ list = []
45
+
46
+ if sitemap_route
47
+
48
+ # if the sitemap defined within config/routes.rb included a block, then, return ALL of the rows associated with
49
+ # sitemap route name. Otherwise, return ALL routes that have NOT BEEN associated with any other sitemap.
50
+ if sitemap_route.sitemap_with_block?
51
+
52
+ # sitemap_route_name MUST MATCH
53
+ #list = self.routes.find_all {|route| !route.is_sitemap? && route.sitemap_route_name.eql?(sitemap_route.sitemap_route_name)}
54
+ list = self.routes.find_all do |route|
55
+ !route.is_sitemap? && route.sitemap_route_name.eql?(sitemap_route.sitemap_route_name)
56
+ end
57
+
58
+ else
59
+
60
+ candidates = self.routes.find_all {|route| !route.is_sitemap?}
61
+
62
+ potential_owners = self.routes.find_all {|route| route.is_sitemap?}
63
+ potential_owners.sort! { |a,b| b.namespace_prefix_underscores <=> a.namespace_prefix_underscores}
64
+
65
+ candidates.each do |candidate|
66
+
67
+ value = nil
68
+ potential_owners.each do |owner|
69
+
70
+ if (!candidate.sitemap_route_name.blank? && owner.sitemap_route_name.eql?(candidate.sitemap_route_name))
71
+
72
+ value = owner
73
+ break
74
+
75
+ elsif (!owner.namespace_prefix.blank? &&
76
+ candidate.name =~ /^#{owner.namespace_prefix}/ &&
77
+ !owner.sitemap_with_block?)
78
+
79
+ value = owner
80
+ break
81
+ end
82
+
83
+ if value.blank?
84
+ potential_owners.each do |owner|
85
+ if (owner.namespace_prefix.blank? && !owner.sitemap_with_block?)
86
+
87
+ value = owner
88
+ break
89
+
90
+ end
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ if !value.blank? && value.name.eql?(sitemap_route.name)
97
+ list.push(candidate)
98
+ end
99
+
100
+ end
101
+
102
+ list.reject! {|route| !self.include_route?(route)}
103
+
104
+ end
105
+
106
+ end
107
+
108
+ return list
109
+ end
110
+
111
+ ##################################################################################
112
+ def route_owner(route)
113
+ value = nil
114
+
115
+ potential_owners = self.routes.find_all {|route| route.is_sitemap?}
116
+ potential_owners.sort! { |a,b| b.namespace_prefix_underscores <=> a.namespace_prefix_underscores}
117
+
118
+ potential_owners.each do |owner|
119
+
120
+ if (!route.sitemap_route_name.blank? && owner.sitemap_route_name.eql?(route.sitemap_route_name))
121
+ value = owner
122
+ break
123
+ elsif !owner.namespace_prefix.blank? && route.name =~ /^#{owner.namespace_prefix}/
124
+ value = owner
125
+ break
126
+ end
127
+
128
+ end
129
+
130
+ if value.blank?
131
+ potential_owners.each do |owner|
132
+ if owner.namespace_prefix.blank? && !owner.sitemap_with_block?
133
+ value = owner
134
+ break
135
+ end
136
+ end
137
+ end
138
+
139
+ return value
140
+ end
141
+
142
+ ##################################################################################
143
+ def sitemap_routes_only
144
+ return self.routes.find_all {|route| route.is_sitemap?}
145
+ end
146
+
147
+ ##################################################################################
148
+ def find_route_via_name(name)
149
+ return self.routes.find {|route| route.name.eql?(name)}
150
+ end
151
+
152
+ ##################################################################################
153
+ def find_route_via_path(path, environment = {})
154
+ method = (environment[:method] || "GET").to_s.upcase
155
+ path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://}
156
+
157
+ begin
158
+ env = Rack::MockRequest.env_for(path, {:method => method})
159
+ rescue URI::InvalidURIError => e
160
+ raise ActionController::RoutingError, e.message
161
+ end
162
+
163
+ req = @request_class.new(env)
164
+ @router.recognize(req) do |route, matches, params|
165
+ params.each do |key, value|
166
+ if value.is_a?(String)
167
+ value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
168
+ params[key] = URI.parser.unescape(value)
169
+ end
170
+ end
171
+
172
+ dispatcher = route.app
173
+ #while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
174
+ while dispatcher.is_a?(ActionDispatch::Routing::Mapper::Constraints) && dispatcher.matches?(env) do
175
+ dispatcher = dispatcher.app
176
+ end
177
+
178
+ if dispatcher.is_a?(ActionDispatch::Routing::RouteSet::Dispatcher) && dispatcher.controller(params, false)
179
+ dispatcher.prepare_params!(params)
180
+ return route
181
+ end
182
+ end
183
+
184
+ raise ActionController::RoutingError, "No route matches #{path.inspect}"
185
+ end
186
+
187
+
188
+
189
+
190
+
191
+ end
192
+
193
+ end
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
@@ -0,0 +1,457 @@
1
+ # DONE i think. need to proof read and re-verify
2
+ require 'active_support/concern'
3
+
4
+ module DuckMap
5
+
6
+ ##################################################################################
7
+ # Module used to add Sitemap methods and attributes to an object.
8
+ module SitemapObject
9
+ extend ActiveSupport::Concern
10
+
11
+ ##################################################################################
12
+ module ClassMethods
13
+
14
+ ##################################################################################
15
+ # @note The syntax is always the same regardless if you define acts_as_sitemap in config/routes.rb or directly
16
+ # on a controller or model.
17
+ #
18
+ # Sets default values, attributes and mappings for sitemap objects. All SitemapObject attributes are maintained
19
+ # in a master Hash. Attributes for each action are stored in the master Hash and accessed via action_name as the key.
20
+ #
21
+ # All attributes are broken into two categories.
22
+ # - Configuration level: These attributes are defined within the config/routes.rb block and are considered
23
+ # global default values for ALL sitemap related classes and objects.
24
+ # - Controller level: These attributes are exactly the same as Configuration level with one difference.
25
+ # They apply to each controller class ONLY. Setting attributes at the controller level will make a copy
26
+ # of the Configuration level attributes and merge with the values you pass to {#acts_as_sitemap acts_as_sitemap}.
27
+ #
28
+ # When a sitemap is created or meta tag data is needed, the controller is "asked" to provide the required data
29
+ # for a specific action on the controller. The mechanism that makes the request is called a "handler". Handlers
30
+ # are mapped to standard Rails controller actions.
31
+ # - The index action is mapped to sitemap_index.
32
+ # - The show action is mapped to sitemap_show.
33
+ #
34
+ # Attributes have two meanings:
35
+ # - If the attribute value is set to a Symbol, then, it is considered to point to an attribute/method name of the target SitemapObject (self).
36
+ # - If the attribute value is set to anything other than a Symbol, then, the value is used "as is".
37
+ #
38
+ # In the example below, we are setting meta data attributes.
39
+ #
40
+ # acts_as_sitemap :index, title: "List of my pages..." #=> static title for the index page.
41
+ # acts_as_sitemap :edit, title: :title #=> returns the value of the title attribute/method.
42
+ # acts_as_sitemap :new, title: "New page" #=> static title for the new page.
43
+ # acts_as_sitemap :show, title: :title #=> returns the value of the title attribute/method.
44
+ #
45
+ # Here, we are setting sitemap attributes.
46
+ #
47
+ # acts_as_sitemap :index, changefreq: "always", priority: "1.0"
48
+ # acts_as_sitemap :edit, changefreq: "never", priority: "0.0"
49
+ # acts_as_sitemap :new, changefreq: "never", priority: "0.0"
50
+ # acts_as_sitemap :show, changefreq: "monthly", priority: "7.0"
51
+ #
52
+ # # here we are telling ALL handlers to get the last modified date
53
+ # # for a sitemap and meta tag from
54
+ # acts_as_sitemap lastmod: :last_updated_at
55
+ #
56
+ # The sitemap / meta tag {DuckMap::Handlers handlers} will attempt to extract attributes values from the first model
57
+ # found on a controller. To make meeting special needs a little easier, you can use a block to return a single model
58
+ # or an Array of model objects to include in your sitemap.
59
+ #
60
+ # This example is worth a few extra words.
61
+ # - When building a sitemap for the index action of a controller, you only need to build one url.
62
+ # - When building a sitemap for the show action of a controller, you will typically need to build an entire set of urls to represent
63
+ # the rows in your table. However, when showing the meta tag data, you will need the attributes from the specific model for the current
64
+ # page.
65
+ #
66
+ # acts_as_sitemap :index do
67
+ # Book.where(title: "my latest book").first
68
+ # end
69
+ #
70
+ # acts_as_sitemap :show do
71
+ # Book.all
72
+ # end
73
+ #
74
+ # Let's say you the show action of your controller just won't fit into the standard behavior
75
+ # and you need to build the url using a custom method. Simply build the method on your object and point to it.
76
+ #
77
+ # acts_as_sitemap :show, canonical: :my_special_method
78
+ #
79
+ # The full syntax would include handlers and segments.
80
+ #
81
+ # acts_as_sitemap :index, title: "My App",
82
+ # changefreq: "always",
83
+ # priority: "1.0",
84
+ # handler: {action_name: :my_index, first_model: false},
85
+ # segments: {id: :my_id, sub_id: :belongs_to_id}
86
+ #
87
+ # However, there are convenience methods to make things a little cleaner.
88
+ #
89
+ # acts_as_sitemap :index, title: "My App", changefreq: "always", priority: "1.0"
90
+ # sitemap_handler :index, action_name: :my_index, first_model: false
91
+ # sitemap_segments :index, id: :my_id, sub_id: :belongs_to_id
92
+ #
93
+ # So far, we have only discussed the attributes that are used by the standard {DuckMap::Handlers handlers}. However,
94
+ # you do have the option of building and using your own handlers to meet special needs or to completely change the default
95
+ # behavior of ALL handlers. Let's say the default behavior for the {DuckMap::Handlers::Index index handler} does not meet your
96
+ # needs. You could build your own index handler and set it in the config/routes.rb
97
+ #
98
+ # MyApp::Application.routes.draw do
99
+ #
100
+ # # defining attributes in config/routes.rb are used globally
101
+ # # your action handler method will be used throughout the entire app.
102
+ # # simply create your own handler method and make it accessible to every controller
103
+ # # in your app. Maybe include in your ApplicationController or something.
104
+ # sitemap_handler :index, action_name: :my_index
105
+ #
106
+ # end
107
+ #
108
+ # @overload acts_as_sitemap(action_name = nil, options = {}, &block)
109
+ # @param [String, Symbol] action_name The action name is used as a key when looking for attributes.
110
+ # When sitemap or meta tag needs data for the index action it
111
+ # will use :index as the key. Omitting the action_name will
112
+ # default to :all which will assign the values you pass to "all"
113
+ # actions for the controller.
114
+ #
115
+ #
116
+ # @param [Hash] options Options hash.
117
+ # - Setting to a Symbol will attempt to access attribute method name on the SitemapObject itself.
118
+ # - Setting to any value other than a Symbol will use that value "as is".
119
+ # @option options [Symbol] :canonical Default value: nil
120
+ # @option options [Symbol] :canonical_host Default value: nil
121
+ # @option options [Symbol] :changefreq Valid static values are:
122
+ # - always
123
+ # - hourly
124
+ # - daily
125
+ # - weekly
126
+ # - monthly
127
+ # - yearly
128
+ # - never
129
+ # @option options [Symbol] :description Default value: :description
130
+ # @option options [Symbol] :handler Sub-hash containing attributes for the handler. See {#sitemap_handler sitemap_handler}
131
+ # @option options [Symbol] :keywords Default value: :keywords
132
+ # @option options [Symbol] :lastmod Default value: :updated_at
133
+ # @option options [Symbol] :priority Valid static values range from 0.0 to 1.0
134
+ # @option options [Symbol] :segments Sub-hash containing attributes for the segments. See {#segments_handler segments_handler}
135
+ # @option options [Symbol] :title Default value: :title
136
+ # @option options [Symbol] :url_format Default value: "html"
137
+ # @option options [Symbol] :url_limit Default value: 50000
138
+ #
139
+ # @param [Block] block A block to assign to all handlers associated with action_name. The
140
+ # block should return an Array of ActiveRecord::Base models. The model objects
141
+ # server as sitemap content for the given action name.
142
+ # @return [Nil]
143
+ def acts_as_sitemap(*args, &block)
144
+ key = :all
145
+
146
+ self.sitemap_attributes_defined = true
147
+
148
+ if args.length > 0
149
+
150
+ if args.first.kind_of?(Symbol)
151
+ key = args.shift
152
+
153
+ elsif args.first.kind_of?(String)
154
+ key = args.shift.to_sym
155
+
156
+ end
157
+
158
+ end
159
+
160
+ values = args.first.kind_of?(Hash) ? args.first : {}
161
+
162
+ # delete method should always return nil if the key doesn't exist
163
+ handler = values.delete(:handler)
164
+ segments = values.delete(:segments)
165
+
166
+ # i'm going to verify and set a default anyway
167
+ handler = handler.kind_of?(Hash) ? handler : {}
168
+
169
+ segments = segments.kind_of?(Hash) ? segments : {}
170
+
171
+ # build a list of keys to work on
172
+ # developer can specify :all, which will build a list of all the existing keys in the Hash
173
+ # otherwise, it will just use the key passed to acts_as_sitemap
174
+ keys = []
175
+ if key == :all
176
+ self.sitemap_attributes.each {|pair| keys.push(pair.first)}
177
+ else
178
+ keys.push(key)
179
+ end
180
+
181
+ # process all of the keys in the list.
182
+ keys.each do |key|
183
+
184
+ # create defaults unless they exist
185
+ unless self.sitemap_attributes[key].kind_of?(Hash)
186
+ self.sitemap_attributes[key] = {}
187
+ end
188
+
189
+ unless self.sitemap_attributes[key][:handler].kind_of?(Hash)
190
+ self.sitemap_attributes[key][:handler] = {}
191
+ end
192
+
193
+ unless self.sitemap_attributes[key][:segments].kind_of?(Hash)
194
+ self.sitemap_attributes[key][:segments] = {}
195
+ end
196
+
197
+ # merge the main hash.
198
+ self.sitemap_attributes[key].merge!(values)
199
+
200
+ # merge the handler
201
+ #unless handler.blank?
202
+ if handler.kind_of?(Hash)
203
+
204
+ if block_given?
205
+
206
+ handler[:block] = block
207
+ end
208
+
209
+ self.sitemap_attributes[key][:handler].merge!(handler)
210
+
211
+ end
212
+
213
+ # merge the segments
214
+ unless segments.blank?
215
+
216
+ self.sitemap_attributes[key][:segments].merge!(segments)
217
+
218
+ end
219
+
220
+ end
221
+
222
+ return nil
223
+ end
224
+
225
+ ##################################################################################
226
+ # @note See {DuckMap::Handlers} for specific details on how each one of these attributes
227
+ # are utilized by each handler.
228
+ #
229
+ # Wrapper for {#acts_as_sitemap acts_as_sitemap} and accepts the same exact arguments.
230
+ # Basically, sitemap_handler simplifies the syntax needed to define a handler
231
+ # for all or a specific action.
232
+ #
233
+ # sitemap_handler :index, action_name: :my_index, first_model: false
234
+ #
235
+ # # is equivalent to:
236
+ # acts_as_sitemap :index, handler: {action_name: :my_index}
237
+ #
238
+ # sitemap_handler :index, instance_var: :my_var
239
+ # sitemap_handler :index, instance_var: "my_var"
240
+ #
241
+ # sitemap_handler :index, model: Book
242
+ # sitemap_handler :index, model: "Book"
243
+ #
244
+ #
245
+ # @overload sitemap_handler(action_name = nil, options = {}, &block)
246
+ # @param [String, Symbol] action_name The action name. See {#acts_as_sitemap acts_as_sitemap}
247
+ # @param [Hash] options Options hash.
248
+ # @option options [Symbol] :action_name The method name to call. Method name MUST exist on your object!
249
+ # @option options [Symbol] :first_model Boolean. Tells the handler to get values from the first available
250
+ # model object found on the controller.
251
+ # @option options [Symbol] :instance_var The model
252
+ # @option options [Symbol] :model A model object expressed as a String or an actual class reference.
253
+ # See {DuckMap::Handlers} for details on how this attribute is utilized.
254
+ # @return [NilClass]
255
+ def sitemap_handler(*args, &block)
256
+ key = args.first.kind_of?(Symbol) ? args.shift : :all
257
+ options = args.first.kind_of?(Hash) ? args.shift : {}
258
+ options = args.last.kind_of?(Hash) ? args.pop : options
259
+ return self.acts_as_sitemap(key, handler: options, &block)
260
+ end
261
+
262
+ ##################################################################################
263
+ # Wrapper for {#acts_as_sitemap acts_as_sitemap} and accepts the same exact arguments.
264
+ # Basically, sitemap_segments simplifies the syntax needed to define segments
265
+ # for all or a specific action.
266
+ #
267
+ # sitemap_segments :index, id: :my_id
268
+ #
269
+ # # is equivalent to:
270
+ # acts_as_sitemap :index, segments: {id: :my_id}
271
+ #
272
+ # @return [NilClass]
273
+ def sitemap_segments(*args, &block)
274
+ key = args.first.kind_of?(Symbol) ? args.shift : :all
275
+ options = args.first.kind_of?(Hash) ? args.shift : {}
276
+ options = args.last.kind_of?(Hash) ? args.pop : options
277
+ return self.acts_as_sitemap(key, segments: options, &block)
278
+ end
279
+
280
+ end
281
+
282
+ ##################################################################################
283
+ # Returns a Hash containing key/value pairs from the current object.
284
+ #
285
+ # This method loops through all of the key/value pairs contained in the attributes Hash.
286
+ #
287
+ # Each pair is inspected.
288
+ # - if the value is a Symbol
289
+ # - the current object is asked if it has a matching method name matching the value.
290
+ # - if true, then, the method is called to obtain a value.
291
+ # - if the new value is NOT nil, then, it is paired with the key of the current pair being processed
292
+ # and assigned to the returning Hash.
293
+ # - otherwise, if the value is not nil, then, it is considered to be a static value and is assigned to the
294
+ # returning Hash using the key of the current pair being processed.
295
+ #
296
+ # # let's pretend this class exists and has the following attributes.
297
+ # object = MyObject.new
298
+ # object.my_title = "my title"
299
+ # object.tags = "this that other"
300
+ # object.content = "this is a test"
301
+ # object.last_updated_at = Time.now
302
+ #
303
+ # attributes = {title: :my_title,
304
+ # keywords: nil,
305
+ # description: "this is my desc",
306
+ # lastmod: :invalid_method_name}
307
+ #
308
+ # values = object.sitemap_capture_attributes(attributes)
309
+ #
310
+ # puts YAML.dump values # => {title => "my title", description => "this is my desc"}
311
+ #
312
+ # The result:
313
+ # - :my_title is a Symbol and attribute exists on object and value is not nil, so, it is included in the returning Hash.
314
+ # - :keywords is nil, so, it is ignored and not included in the returning Hash.
315
+ # - :description is a String and considered a static value, so, it is included in the returning Hash "as is".
316
+ # - :my_title is a Symbol, however, a matching attribute/method does NOT exist on the target object, so,
317
+ # it is ignored and not included in the returning Hash.
318
+ #
319
+ # @param [Hash] options Options hash. Can be any combination of key/value pairs (one-dimensional).
320
+ # return [Hash]
321
+ def sitemap_capture_attributes(attributes = {})
322
+ values = {}
323
+
324
+ attributes.each do |pair|
325
+
326
+ # if the value of the pair is a Symbol, then, it is implied to be
327
+ # an attribute of the object we are working on.
328
+ # therefore, we will attempt to get the value of that attribute from the object.
329
+ if pair.last.kind_of?(Symbol)
330
+
331
+ # if the object has the attribute/method we are looking for, then, capture the value
332
+ # by calling the attribute/method and assign the return value to the return Hash
333
+ # using pair.first as the key.
334
+ if self.respond_to?(pair.last)
335
+
336
+ # ask the object for the attribute value
337
+ value = self.send(pair.last)
338
+
339
+ # ignore nil values
340
+ unless value.blank?
341
+ values[pair.first] = value
342
+ end
343
+
344
+ end
345
+
346
+ elsif !pair.last.blank?
347
+ values[pair.first] = pair.last
348
+ end
349
+
350
+ end
351
+
352
+ return values
353
+ end
354
+
355
+ ##################################################################################
356
+ # Segment keys are placeholders for the values that are plugged into a named route when it is constructed.
357
+ #
358
+ # The following Rails route has a two segment keys: :id and :format.
359
+ #
360
+ # book GET /books/:id(.:format) books#show
361
+ #
362
+ # :id is the row.id of a Book and :format is the extension to be used when constructing a path or url.
363
+ #
364
+ # book_path(1) #=> /book/1
365
+ # book_path(1, "html") #=> /book/1.html
366
+ # book_path(id: 1, format: "html") #=> /book/1.html
367
+ # book_path(id: 2, format: "xml") #=> /book/2.xml
368
+ #
369
+ # sitemap_capture_segments attempts to populate a Hash with values associated with the required segment keys.
370
+ #
371
+ # row = Book.create(title: "Duck is a self-proclaimed resident moron...")
372
+ # puts row.id #=> 1
373
+ # row.sitemap_capture_segments(nil, [:id]) #=> {:id => 1}
374
+ #
375
+ # You have the ability to map attributes of an object to segment keys. This could be useful for routes
376
+ # that do not follow standard convention or cases where you have some deeply nested resources.
377
+ #
378
+ # class BooksController < ApplicationController
379
+ # sitemap_segments :show, id: :my_id
380
+ # end
381
+ #
382
+ # class Book < ActiveRecord::Base
383
+ # attr_accessible :my_id, :author, :title
384
+ #
385
+ # before_save :generate_my_id
386
+ #
387
+ # def generate_my_id
388
+ # # do some magic
389
+ # self.my_id = 2
390
+ # end
391
+ #
392
+ # end
393
+ #
394
+ # row = Book.create(title: "Please ignore the first title :)")
395
+ # puts row.id #=> 1
396
+ # puts row.my_id #=> 2
397
+ # # normally, you would get the attributes via:
398
+ # # controller.sitemap_attributes("show")[:segments]
399
+ # attributes = {id: :my_id}
400
+ # row.sitemap_capture_segments(attributes, [:id]) #=> {:id => 2}
401
+ #
402
+ # Segment values are obtained in two stages.
403
+ # - Stage one asks the current object (controller or model) for attributes from segment_mappings and
404
+ # places those key/values in the returning hash.
405
+ #
406
+ # - Stage two asks the current object (controller or model) for attributes from segments array
407
+ # that have not already been found via segment_mappings and places those key/values in the returning hash.
408
+ #
409
+ # @param [Hash] segment_mappings A Hash containing one-to-one attribute mappings for segment keys to object attributes.
410
+ # @param [Array] segments The segments Array of a Rails Route.
411
+ # return [Hash]
412
+ def sitemap_capture_segments(segment_mappings = {}, segments = [])
413
+ values = {}
414
+
415
+ # do nothing if there are no segments to work on
416
+ if segments.kind_of?(Array)
417
+
418
+ # first, look for mappings
419
+ unless segment_mappings.blank?
420
+ segments.each do |key|
421
+
422
+ attribute_name = segment_mappings[key.to_sym].blank? ? key : segment_mappings[key.to_sym]
423
+
424
+ if self.respond_to?(attribute_name)
425
+ values[key] = self.send(attribute_name)
426
+ end
427
+
428
+ end
429
+ end
430
+
431
+ # second, look for attributes that have not already been found.
432
+ segments.each do |key|
433
+
434
+ unless values.has_key?(key)
435
+ if self.respond_to?(key)
436
+ values[key] = self.send(key)
437
+ end
438
+ end
439
+
440
+ end
441
+
442
+ end
443
+
444
+ return values
445
+ end
446
+
447
+ end
448
+ end
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+