ginjo-rfm 2.1.7 → 3.0.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.
@@ -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