ginjo-rfm 2.1.7 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -67,23 +67,13 @@ module Rfm
67
67
  #
68
68
  # This sample code gets a database object representing the Customers database on the FileMaker server.
69
69
  def initialize(*args) #name, server_obj, acnt=nil, pass=nil
70
- options = get_config(*args)
71
- rfm_metaclass.instance_variable_set :@server, (options[:objects].delete_at(0) || options[:server_object])
72
- options = get_config(options)
73
-
74
- config sanitize_config(options, {}, true)
75
- config :parent=> 'server'
76
- config :database=> options[:strings].delete_at(0) || options[:database]
77
- config :account_name=> options[:strings].delete_at(0) || options[:account_name]
78
- config :password=> options[:strings].delete_at(0) || options[:password]
79
-
70
+ config(*args)
80
71
  raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Database has no name. Attempted name '#{state[:database]}'.") if state[:database].to_s == ''
81
-
82
72
  @layouts = Rfm::Factory::LayoutFactory.new(server, self)
83
73
  @scripts = Rfm::Factory::ScriptFactory.new(server, self)
84
74
  end
85
75
 
86
- meta_attr_reader :server
76
+ meta_attr_accessor :server
87
77
  attr_reader :layouts, :scripts
88
78
  # Not sure if these writers are ever used
89
79
  #attr_writer :account_name, :password
@@ -97,9 +87,18 @@ module Rfm
97
87
  def password; state[:password]; end
98
88
  def password=(x); config :password=>x; end
99
89
 
100
- def state(*args)
101
- get_config(*args)
102
- end
90
+ def config(*args)
91
+ super(:capture_strings_with=>[:database, :account_name, :password])
92
+ super(*args) do |params|
93
+ (self.server = params[:objects][0]) if params && params[:objects] && params[:objects][0] && params[:objects][0].is_a?(Rfm::Server)
94
+ end
95
+ end
96
+
97
+ alias_method :server_orig, :server
98
+ def server
99
+ server_orig || (self.server = Rfm::Server.new(state[:host], state[:account_name], state[:password], self))
100
+ end
101
+
103
102
 
104
103
  # Access the Layout object representing a layout in this database. For example:
105
104
  #
@@ -115,7 +114,7 @@ module Rfm
115
114
  # def [](layout_name)
116
115
  # self.layout[layout_name]
117
116
  # end
118
- def_delegator :layouts, :[]
117
+ def_delegators :layouts, :[], :modelize, :models # modelize & models acquired from Rfm::Base
119
118
 
120
119
  end
121
120
  end
@@ -1,3 +1,4 @@
1
+ require 'delegate'
1
2
  module Rfm
2
3
  # The Layout object represents a single FileMaker Pro layout. You use it to interact with
3
4
  # records in FileMaker. *All* access to FileMaker data is done through a layout, and this
@@ -120,6 +121,41 @@ module Rfm
120
121
 
121
122
  class Layout
122
123
  include Config
124
+
125
+ meta_attr_accessor :db
126
+ attr_reader :field_mapping
127
+ attr_writer :resultset_meta
128
+ def_delegator :db, :server
129
+ #alias_method :database, :db # This fails if db object hasn't been set yet with meta_attr_accessor
130
+ def database; db; end
131
+ attr_accessor :model #, :parent_layout, :subs
132
+ def_delegators :meta, :field_controls, :value_lists
133
+ def_delegators :resultset_meta, :date_format, :time_format, :timestamp_format, :field_meta, :portal_meta, :table
134
+
135
+ # Methods that must be kept after rewrite!!!
136
+ #
137
+ # field_mapping
138
+ # db (database)
139
+ # name
140
+ # resultset_meta
141
+ # date_format
142
+ # time_format
143
+ # timestamp_format
144
+ # field_meta
145
+ # field_controls
146
+ # field_names
147
+ # field_names_no_load
148
+ # value_lists
149
+ # count
150
+ # total_count
151
+ # portal_meta
152
+ # portal_meta_no_load
153
+ # portal_names
154
+ # table
155
+ # table_no_load
156
+ # server
157
+
158
+
123
159
 
124
160
  # Initialize a layout object. You never really need to do this. Instead, just do this:
125
161
  #
@@ -135,305 +171,219 @@ module Rfm
135
171
  # myServer = Rfm::Server.new(...)
136
172
  # myLayout = myServer["Customers"]["Details"]
137
173
  def initialize(*args) #name, db_obj
138
- options = get_config(*args)
139
- rfm_metaclass.instance_variable_set :@db, (options[:objects].delete_at(0) || options[:database_object])
140
- config :parent=> 'db'
141
- options = get_config(options)
142
-
143
- config sanitize_config(options, {}, true)
144
- config :layout => options[:strings].delete_at(0) || options[:layout]
145
-
146
- raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Layout has no name. Attempted name '#{state[:layout]}'.") if state[:layout].to_s == ''
147
-
174
+ # self.subs ||= []
175
+ config(*args)
176
+ raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Layout has no name. Attempted name '#{state[:layout]}'.") if state[:layout].to_s == ''
148
177
  @loaded = false
149
- @field_controls = Rfm::CaseInsensitiveHash.new
150
- @value_lists = Rfm::CaseInsensitiveHash.new
151
- # @portal_meta = nil
152
- # @field_names = nil
153
- #@ignore_bad_data = (db_obj.server.state[:ignore_bad_data] rescue nil)
178
+ @meta = Metadata::LayoutMeta.new(self)
179
+ self
154
180
  end
155
-
156
- meta_attr_reader :db
157
- attr_reader :field_mapping
158
- attr_writer :field_names, :portal_meta, :table
159
- def_delegator :db, :server
160
- alias_method :database, :db
161
-
162
- # This method may be obsolete, since the option can now be set with #config.
163
- def ignore_bad_data(val = nil)
164
- (config :ignore_bad_data => val) unless val.nil?
165
- state[:ignore_bad_data]
181
+
182
+ def config(*args)
183
+ super(:capture_strings_with=>[:layout])
184
+ super(*args) do |params|
185
+ (self.name = params[:strings][0]) if params && params[:strings] && params[:strings].any?
186
+ (self.db = params[:objects][0]) if params && params[:objects] && params[:objects][0] && params[:objects][0].is_a?(Rfm::Database)
187
+ end
166
188
  end
167
189
 
168
- # These methods are to be inclulded in Layout and SubLayout, so that
169
- # they have their own discrete 'self' in the master class and the subclass.
170
- # This means these methods will not get forwarded, and will talk to the correct
171
- # variables & objects of the correct self.
172
- # Do not get or set instance variables in Layout from other objects directly,
173
- # always use getter & setter methods.
174
- # This all means that any chain of methods that want to refer ultimately to Sublayout, must all be defined or included in Sublayout
175
- module LayoutModule
190
+ alias_method :db_orig, :db
191
+ def db
192
+ db_orig || (self.db = Rfm::Database.new(state[:database], state[:account_name], state[:password], self))
193
+ end
176
194
 
177
- # Returns a ResultSet object containing _every record_ in the table associated with this layout.
178
- def all(options = {})
179
- get_records('-findall', {}, options)
180
- end
181
-
182
- # Returns a ResultSet containing a single random record from the table associated with this layout.
183
- def any(options = {})
184
- get_records('-findany', {}, options)
185
- end
186
-
187
- # Finds a record. Typically you will pass in a hash of field names and values. For example:
188
- #
189
- # myLayout.find({"First Name" => "Bill"})
190
- #
191
- # Values in the hash work just like value in FileMaker's Find mode. You can use any special
192
- # symbols (+==+, +...+, +>+, etc...).
193
- #
194
- # Create a Filemaker 'omit' request by including an :omit key with a value of true.
195
- #
196
- # myLayout.find :field1 => 'val1', :field2 => 'val2', :omit => true
197
- #
198
- # Create multiple Filemaker find requests by passing an array of hashes to the #find method.
199
- #
200
- # myLayout.find [{:field1 => 'bill', :field2 => 'admin'}, {:field3 => 'inactive', :omit => true}, ...]
201
- #
202
- # If the value of a field in a find request is an array of strings, the string values will be logically OR'd in the query.
203
- #
204
- # myLayout.find :fieldOne => ['bill','mike','bob'], :fieldTwo =>'staff'
205
- #
206
- # If you pass anything other than a hash or an array as the first parameter, it is converted to a string and
207
- # assumed to be FileMaker's internal id for a record (the recid).
208
- #
209
- # myLayout.find 54321
210
- #
211
- def find(find_criteria, options = {})
212
- #puts "layout.find-#{self.object_id}"
213
- options.merge!({:field_mapping => field_mapping.invert}) if field_mapping
214
- get_records(*Rfm::CompoundQuery.new(find_criteria, options))
215
- end
216
-
217
- # Access to raw -findquery command.
218
- def query(query_hash, options = {})
219
- get_records('-findquery', query_hash, options)
220
- end
221
-
222
- # Updates the contents of the record whose internal +recid+ is specified. Send in a hash of new
223
- # data in the +values+ parameter. Returns a RecordSet containing the modified record. For example:
224
- #
225
- # recid = myLayout.find({"First Name" => "Bill"})[0].record_id
226
- # myLayout.edit(recid, {"First Name" => "Steve"})
227
- #
228
- # The above code would find the first record with _Bill_ in the First Name field and change the
229
- # first name to _Steve_.
230
- def edit(recid, values, options = {})
231
- get_records('-edit', {'-recid' => recid}.merge(values), options)
232
- #get_records('-edit', {'-recid' => recid}.merge(expand_repeats(values)), options) # attempt to set repeating fields.
233
- end
234
-
235
- # Creates a new record in the table associated with this layout. Pass field data as a hash in the
236
- # +values+ parameter. Returns the newly created record in a RecordSet. You can use the returned
237
- # record to, ie, discover the values in auto-enter fields (like serial numbers).
238
- #
239
- # For example:
240
- #
241
- # result = myLayout.create({"First Name" => "Jerry", "Last Name" => "Robin"})
242
- # id = result[0]["ID"]
243
- #
244
- # The above code adds a new record with first name _Jerry_ and last name _Robin_. It then
245
- # puts the value from the ID field (a serial number) into a ruby variable called +id+.
246
- def create(values, options = {})
247
- get_records('-new', values, options)
248
- end
249
-
250
- # Deletes the record with the specified internal recid. Returns a ResultSet with the deleted record.
251
- #
252
- # For example:
253
- #
254
- # recid = myLayout.find({"First Name" => "Bill"})[0].record_id
255
- # myLayout.delete(recid)
256
- #
257
- # The above code finds every record with _Bill_ in the First Name field, then deletes the first one.
258
- def delete(recid, options = {})
259
- get_records('-delete', {'-recid' => recid}, options)
260
- return nil
261
- end
262
-
263
- # Retrieves metadata only, with an empty resultset.
264
- def view(options = {})
265
- get_records('-view', {}, options)
266
- end
267
-
268
- def get_records(action, extra_params = {}, options = {})
269
- # TODO: The grammar stuff here won't work properly until you handle config between
270
- # models/sublayouts/layout/server (Is this done now?).
271
- grammar_option = state(options)[:grammar]
272
- options.merge!(:grammar=>grammar_option) if grammar_option
273
- #include_portals = options[:include_portals] ? options.delete(:include_portals) : nil
274
- include_portals = !options[:ignore_portals]
275
-
276
- # Apply mapping from :field_mapping, to send correct params in URL.
277
- prms = params.merge(extra_params)
278
- map = field_mapping.invert
279
- options.merge!({:field_mapping => map}) if map
280
- # TODO: Make this part handle string AND symbol keys.
281
- #map.each{|k,v| prms[k]=prms.delete(v) if prms[v]}
282
- prms.dup.each_key{|k| prms[map[k.to_s]]=prms.delete(k) if map[k.to_s]}
283
-
284
- xml_response = server.connect(state[:account_name], state[:password], action, prms, options).body
285
- Rfm::Resultset.new(xml_response, self, include_portals)
286
- end
287
-
288
- def params
289
- {"-db" => db.name, "-lay" => self.name}
290
- end
291
-
292
-
293
- # Intended to set each repeat individually but doesn't work with FM
294
- def expand_repeats(hash)
295
- hash.each do |key,val|
296
- if val.kind_of? Array
297
- val.each_with_index{|v, i| hash["#{key}(#{i+1})"] = v}
298
- hash.delete(key)
299
- end
300
- end
301
- hash
302
- end
303
-
304
- # Intended to brute-force repeat setting but doesn't work with FM
305
- def join_repeats(hash)
306
- hash.each do |key,val|
307
- if val.kind_of? Array
308
- hash[key] = val.join('\x1D')
309
- end
310
- end
311
- hash
312
- end
313
-
314
- def name; state[:layout].to_s; end
315
-
316
- def state(*args)
317
- get_config(*args)
318
- end
319
-
320
- end # LayoutModule
321
-
322
- include LayoutModule
323
-
324
-
325
- ###
326
- def view_meta
327
- @view_meta ||= view
328
- end
329
- def date_format
330
- @date_format ||= view_meta.date_format
331
- end
332
- def time_format
333
- @time_format ||= view_meta.time_format
334
- end
335
- def timestamp_format
336
- @timestamp_format ||= view_meta.timestamp_format
337
- end
338
- def field_meta
339
- @field_meta ||= view_meta.field_meta
340
- end
341
- ###
195
+ # Returns a ResultSet object containing _every record_ in the table associated with this layout.
196
+ def all(options = {})
197
+ get_records('-findall', {}, options)
198
+ end
342
199
 
200
+ # Returns a ResultSet containing a single random record from the table associated with this layout.
201
+ def any(options = {})
202
+ get_records('-findany', {}, options)
203
+ end
204
+
205
+ # Finds a record. Typically you will pass in a hash of field names and values. For example:
206
+ #
207
+ # myLayout.find({"First Name" => "Bill"})
208
+ #
209
+ # Values in the hash work just like value in FileMaker's Find mode. You can use any special
210
+ # symbols (+==+, +...+, +>+, etc...).
211
+ #
212
+ # Create a Filemaker 'omit' request by including an :omit key with a value of true.
213
+ #
214
+ # myLayout.find :field1 => 'val1', :field2 => 'val2', :omit => true
215
+ #
216
+ # Create multiple Filemaker find requests by passing an array of hashes to the #find method.
217
+ #
218
+ # myLayout.find [{:field1 => 'bill', :field2 => 'admin'}, {:field3 => 'inactive', :omit => true}, ...]
219
+ #
220
+ # If the value of a field in a find request is an array of strings, the string values will be logically OR'd in the query.
221
+ #
222
+ # myLayout.find :fieldOne => ['bill','mike','bob'], :fieldTwo =>'staff'
223
+ #
224
+ # If you pass anything other than a hash or an array as the first parameter, it is converted to a string and
225
+ # assumed to be FileMaker's internal id for a record (the recid).
226
+ #
227
+ # myLayout.find 54321
228
+ #
229
+ def find(find_criteria, options = {})
230
+ #puts "layout.find-#{self.object_id}"
231
+ options.merge!({:field_mapping => field_mapping.invert}) if field_mapping
232
+ get_records(*Rfm::CompoundQuery.new(find_criteria, options))
233
+ end
234
+
235
+ # Access to raw -findquery command.
236
+ def query(query_hash, options = {})
237
+ get_records('-findquery', query_hash, options)
238
+ end
239
+
240
+ # Updates the contents of the record whose internal +recid+ is specified. Send in a hash of new
241
+ # data in the +values+ parameter. Returns a RecordSet containing the modified record. For example:
242
+ #
243
+ # recid = myLayout.find({"First Name" => "Bill"})[0].record_id
244
+ # myLayout.edit(recid, {"First Name" => "Steve"})
245
+ #
246
+ # The above code would find the first record with _Bill_ in the First Name field and change the
247
+ # first name to _Steve_.
248
+ def edit(recid, values, options = {})
249
+ get_records('-edit', {'-recid' => recid}.merge(values), options)
250
+ #get_records('-edit', {'-recid' => recid}.merge(expand_repeats(values)), options) # attempt to set repeating fields.
251
+ end
343
252
 
344
- def field_controls
345
- load unless @loaded
346
- @field_controls
253
+ # Creates a new record in the table associated with this layout. Pass field data as a hash in the
254
+ # +values+ parameter. Returns the newly created record in a RecordSet. You can use the returned
255
+ # record to, ie, discover the values in auto-enter fields (like serial numbers).
256
+ #
257
+ # For example:
258
+ #
259
+ # result = myLayout.create({"First Name" => "Jerry", "Last Name" => "Robin"})
260
+ # id = result[0]["ID"]
261
+ #
262
+ # The above code adds a new record with first name _Jerry_ and last name _Robin_. It then
263
+ # puts the value from the ID field (a serial number) into a ruby variable called +id+.
264
+ def create(values, options = {})
265
+ get_records('-new', values, options)
347
266
  end
348
267
 
349
- def field_names
350
- load unless @field_names
351
- @field_names
352
- end
353
-
354
- def field_names_no_load
355
- @field_names
356
- end
268
+ # Deletes the record with the specified internal recid. Returns a ResultSet with the deleted record.
269
+ #
270
+ # For example:
271
+ #
272
+ # recid = myLayout.find({"First Name" => "Bill"})[0].record_id
273
+ # myLayout.delete(recid)
274
+ #
275
+ # The above code finds every record with _Bill_ in the First Name field, then deletes the first one.
276
+ def delete(recid, options = {})
277
+ get_records('-delete', {'-recid' => recid}, options)
278
+ return nil
279
+ end
357
280
 
358
- def value_lists
359
- load unless @loaded
360
- @value_lists
281
+ # Retrieves metadata only, with an empty resultset.
282
+ def view(options = {})
283
+ get_records('-view', {}, options)
361
284
  end
362
285
 
286
+ # Get the foundset_count only given criteria & options.
363
287
  def count(find_criteria, options={})
364
288
  find(find_criteria, options.merge({:max_records => 0})).foundset_count
365
289
  end
366
290
 
367
- def total_count
368
- view.total_count
291
+ def get_records(action, extra_params = {}, options = {})
292
+ # TODO: The grammar stuff here won't work properly until you handle config between
293
+ # models/sublayouts/layout/server (Is this done now?).
294
+ grammar_option = state(options)[:grammar]
295
+ options.merge!(:grammar=>grammar_option) if grammar_option
296
+ template = options.delete :template
297
+
298
+ # # TODO: Remove this code it is no longer used.
299
+ # #include_portals = options[:include_portals] ? options.delete(:include_portals) : nil
300
+ # include_portals = !options[:ignore_portals]
301
+
302
+ # Apply mapping from :field_mapping, to send correct params in URL.
303
+ prms = params.merge(extra_params)
304
+ map = field_mapping.invert
305
+ options.merge!({:field_mapping => map}) if map && !map.empty?
306
+ # TODO: Make this part handle string AND symbol keys.
307
+ #map.each{|k,v| prms[k]=prms.delete(v) if prms[v]}
308
+ prms.dup.each_key{|k| prms[map[k.to_s]]=prms.delete(k) if map[k.to_s]}
309
+
310
+ #c = Connection.new(action, prms, options, state.merge(:parent=>self))
311
+ c = Connection.new(action, prms, options, self)
312
+ rslt = c.parse(template || :fmresultset, Rfm::Resultset.new(self, self))
313
+ capture_resultset_meta(rslt) unless @resultset_meta
314
+ rslt
315
+ end
316
+
317
+ def params
318
+ {"-db" => state[:database], "-lay" => self.name}
319
+ end
320
+
321
+ def name; state[:layout].to_s; end
322
+
323
+
324
+
325
+
326
+ ### Metadata from Layout ###
327
+
328
+ def meta
329
+ @loaded ? @meta : load_layout
330
+ end
331
+
332
+ def field_names
333
+ case
334
+ when @loaded; meta.field_names
335
+ when @resultset_meta; resultset_meta.field_names
336
+ else meta.field_names
337
+ end
369
338
  end
370
339
 
371
- def portal_meta
372
- @portal_meta ||= view.portal_meta
340
+ def field_keys
341
+ case
342
+ when @loaded; @meta.field_keys
343
+ when @resultset_meta; @resultset_meta.field_keys
344
+ else meta.field_keys
345
+ end
373
346
  end
374
347
 
375
- def portal_meta_no_load
376
- @portal_meta
377
- end
378
348
 
349
+
350
+
351
+ ### Metadata from Resultset ###
352
+
353
+ def resultset_meta
354
+ @resultset_meta || view.meta
355
+ end
356
+
357
+ # Should always refresh
358
+ def total_count
359
+ view.total_count
360
+ end
361
+
362
+ def capture_resultset_meta(resultset)
363
+ (@resultset_meta = resultset.clone.replace([])) #unless @resultset_meta
364
+ @resultset_meta = resultset.meta
365
+ end
366
+
379
367
  def portal_names
380
- portal_meta.keys
368
+ return 'UNDER-CONTSTRUCTION'
381
369
  end
370
+
382
371
 
383
- def table
384
- @table ||= view.table
385
- end
386
372
 
387
- def table_no_load
388
- @table
389
- end
373
+
374
+ ### Utility ###
390
375
 
391
- def load
392
- @loaded = true
393
- fmpxmllayout = db.server.load_layout(self)
394
- doc = XmlParser.new(fmpxmllayout.body, :namespace=>false, :parser=>server.state[:parser])
395
-
396
- # check for errors
397
- error = doc['FMPXMLLAYOUT']['ERRORCODE'].to_s.to_i
398
- raise Rfm::Error::FileMakerError.getError(error) if error != 0
399
-
400
- # process valuelists
401
- vlists = [doc['FMPXMLLAYOUT']['VALUELISTS']['VALUELIST']].flatten.compact
402
- if !vlists.blank? #root.elements['VALUELISTS'].size > 0
403
- vlists.each {|valuelist|
404
- name = valuelist['NAME']
405
- @value_lists[name] = [valuelist['VALUE']].flatten.collect{|value|
406
- Rfm::Metadata::ValueListItem.new(value['__content__'], value['DISPLAY'], name)
407
- } rescue []
408
- }
409
- @value_lists.freeze
410
- end
411
-
412
- # process field controls
413
- [doc['FMPXMLLAYOUT']['LAYOUT']['FIELD']].flatten.compact.each {|field|
414
- name = field_mapping[field['NAME']] || field['NAME']
415
- style = field['STYLE']
416
- type = style['TYPE']
417
- value_list_name = style['VALUELIST']
418
- value_list = @value_lists[value_list_name] if !value_list_name.blank?
419
- field_control = Rfm::Metadata::FieldControl.new(name, type, value_list_name, value_list)
420
- existing = @field_controls[name]
421
- if existing
422
- if existing.kind_of?(Array)
423
- existing << field_control
424
- else
425
- @field_controls[name] = Array[existing, field_control]
426
- end
427
- else
428
- @field_controls[name] = field_control
429
- end
430
- }
431
- @field_names ||= @field_controls.collect{|k,v| v.name rescue v[0].name}
432
- @field_controls.freeze
376
+ def load_layout
377
+ @loaded = true # This is first so parsing call to 'meta' wont cause infinite loop.
378
+ connection = Connection.new('-view', {'-db' => state[:database], '-lay' => name}, {:grammar=>'FMPXMLLAYOUT'}, self)
379
+ rslt = connection.parse(:fmpxmllayout, self)
380
+ #puts ["LAYOUT#load_layout result", rslt.class].join(', ')
381
+ # @loaded = true
382
+ @meta
433
383
  end
434
384
 
435
385
  def field_mapping
436
- @field_mapping ||= load_field_mapping(get_config[:field_mapping])
386
+ @field_mapping ||= load_field_mapping(state[:field_mapping])
437
387
  end
438
388
 
439
389
  def load_field_mapping(mapping={})
@@ -443,8 +393,31 @@ module Rfm
443
393
  end
444
394
  mapping
445
395
  end
396
+
397
+ # Creates new class with layout name.
398
+ def modelize
399
+ @model ||= (
400
+ model_name = name.to_s.gsub(/\W|_/, ' ').title_case.gsub(/\s/,'')
401
+ #model_class = eval("::" + model_name + "= Class.new(Rfm::Base)")
402
+ model_class = Rfm.const_defined?(model_name) ? Rfm.const_get(model_name) : Rfm.const_set(model_name, Class.new(Rfm::Base))
403
+ model_class.class_exec(self) do |layout_obj|
404
+ @layout = layout_obj
405
+ end
406
+ model_class.config :parent=>'@layout'
407
+ model_class
408
+ )
409
+ # rescue StandardError, SyntaxError
410
+ # puts "Error in layout#modelize: #{$!}"
411
+ # nil
412
+ end
413
+
414
+ def models
415
+ #subs.collect{|s| s.model}
416
+ [@model]
417
+ end
418
+
446
419
 
447
- private :load, :get_records, :params
420
+ private :load_layout, :get_records, :params
448
421
 
449
422
 
450
423
  end # Layout