Class: Rfm::Layout

Inherits:
Object show all
Includes:
Config
Defined in:
lib/rfm/layout.rb

Overview

The Layout object represents a single FileMaker Pro layout. You use it to interact with records in FileMaker. All access to FileMaker data is done through a layout, and this layout determines which table you actually hit (since every layout is explicitly associated with a particular table – see FileMakers Layout->Layout Setup dialog box). You never specify table information directly in RFM.

Also, the layout determines which fields will be returned. If a layout contains only three fields from a large table, only those three fields are returned. If a layout includes related fields from another table, they are returned as well. And if the layout includes portals, all data in the portals is returned (see Record::portal for details).

As such, you can significantly improve performance by limiting what you put on the layout.

Using Layouts

The Layout object is where you get most of your work done. It includes methods for all FileMaker actions:

  • Layout::all

  • Layout::any

  • Layout::find

  • Layout::edit

  • Layout::create

  • Layout::delete

Running Scripts

In FileMaker, execution of a script must accompany another action. For example, to run a script called _Remove Duplicates_ with a found set that includes everybody named Bill, do this:

myLayout.find({"First Name" => "Bill"}, :post_script => "Remove Duplicates")

Controlling When the Script Runs

When you perform an action in FileMaker, it always executes in this order:

  1. Perform any find

  2. Sort the records

  3. Return the results

You can control when in the process the script runs. Each of these options is available:

  • post_script tells FileMaker to run the script after finding and sorting

  • pre_find_script tells FileMaker to run the script before finding

  • pre_sort_script tells FileMaker to run the script before sorting, but after finding

Passing Parameters to a Script

If you want to pass a parameter to the script, use the options above, but supply an array value instead of a single string. For example:

myLayout.find({"First Name" => "Bill"}, :post_script => ["Remove Duplicates", 10])

This sample runs the script called “Remove Duplicates” and passes it the value 10 as its script parameter.

Common Options

Most of the methods on the Layout object accept an optional hash of options to manipulate the action. For example, when you perform a find, you will typiclaly get back all matching records. If you want to limit the number of records returned, you can do this:

myLayout.find({"First Name" => "Bill"}, :max_records => 100)

The :max_records option tells FileMaker to limit the number of records returned.

This is the complete list of available options:

  • max_records tells FileMaker how many records to return

  • skip_records tells FileMaker how many records in the found set to skip, before returning results; this is typically combined with max_records to “page” through records

  • sort_field tells FileMaker to sort the records by the specified field

  • sort_order can be descend or ascend and determines the order of the sort when sort_field is specified

  • post_script tells FileMaker to perform a script after carrying out the action; you can pass the script name, or a two-element array, with the script name first, then the script parameter

  • pre_find_script is like post_script except the script runs before any find is performed

  • pre_sort_script is like pre_find_script except the script runs after any find and before any sort

  • response_layout tells FileMaker to switch layouts before producing the response; this is useful when you need a field on a layout to perform a find, edit, or create, but you want to improve performance by not including the field in the result

  • logical_operator can be and or or and tells FileMaker how to process multiple fields in a find request

  • modification_id lets you pass in the modification id from a Record object with the request; when you do, the action will fail if the record was modified in FileMaker after it was retrieved by RFM but before the action was run

Attributes

The Layout object has a few useful attributes:

  • name is the name of the layout

  • field_controls is a hash of FieldControl objects, with the field names as keys. FieldControl’s tell you about the field on the layout: how is it formatted and what value list is assigned

Note: It is possible to put the same field on a layout more than once. When this is the case, the value in field_controls for that field is an array with one element representing each instance of the field.

  • value_lists is a hash of arrays. The keys are value list names, and the values in the hash are arrays containing the actual value list items. value_lists will include every value list that is attached to any field on the layout

Constant Summary

Constants included from Config

Config::CONFIG_DONT_STORE, Config::CONFIG_KEYS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Config

#config_clear, #get_config, #log, #state

Constructor Details

#initialize(*args) ⇒ Layout

Initialize a layout object. You never really need to do this. Instead, just do this:

myServer = Rfm::Server.new(...)
myDatabase = myServer["Customers"]
myLayout = myDatabase["Details"]

This sample code gets a layout object representing the Details layout in the Customers database on the FileMaker server.

In case it isn’t obvious, this is more easily expressed this way:

myServer = Rfm::Server.new(...)
myLayout = myServer["Customers"]["Details"]


177
178
179
180
181
182
183
184
# File 'lib/rfm/layout.rb', line 177

def initialize(*args) #name, db_obj
  # self.subs ||= []
  config(*args)
  raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Layout has no name. Attempted name '#{state[:layout]}'.") if state[:layout].to_s == ''
  @loaded = false
  @meta = Metadata::LayoutMeta.new(self)
  self
end

Instance Attribute Details

#field_mappingObject (readonly)

Returns the value of attribute field_mapping.



127
128
129
# File 'lib/rfm/layout.rb', line 127

def field_mapping
  @field_mapping
end

#modelObject

, :parent_layout, :subs



136
137
138
# File 'lib/rfm/layout.rb', line 136

def model
  @model
end

#resultset_metaObject

Metadata from Resultset ###



381
382
383
384
# File 'lib/rfm/layout.rb', line 381

def resultset_meta
  #@resultset_meta || view.meta
  resultset_meta_valid? ? @resultset_meta : view.meta
end

Instance Method Details

#all(options = {}) ⇒ Object

Returns a ResultSet object containing _every record_ in the table associated with this layout.



200
201
202
# File 'lib/rfm/layout.rb', line 200

def all(options = {})
  get_records('-findall', {}, options)
end

#any(options = {}) ⇒ Object

Returns a ResultSet containing a single random record from the table associated with this layout.



205
206
207
# File 'lib/rfm/layout.rb', line 205

def any(options = {})
  get_records('-findany', {}, options)
end

#capture_resultset_meta(resultset) ⇒ Object



397
398
399
400
# File 'lib/rfm/layout.rb', line 397

def capture_resultset_meta(resultset)
  (@resultset_meta = resultset.clone.replace([])) #unless @resultset_meta
  @resultset_meta = resultset.meta
end

#config(*args) ⇒ Object



186
187
188
189
190
191
192
# File 'lib/rfm/layout.rb', line 186

def config(*args)
  super(:capture_strings_with=>[:layout])
  super(*args) do |params|
    (self.name = params[:strings][0]) if params && params[:strings] && params[:strings].any?
    (self.db = params[:objects][0]) if params && params[:objects] && params[:objects][0] && params[:objects][0].is_a?(Rfm::Database)
  end
end

#count(find_criteria, options = {}) ⇒ Object

Get the foundset_count only given criteria & options.



291
292
293
# File 'lib/rfm/layout.rb', line 291

def count(find_criteria, options={})
  find(find_criteria, options.merge({:max_records => 0})).foundset_count
end

#create(values, options = {}) ⇒ Object

Creates a new record in the table associated with this layout. Pass field data as a hash in the values parameter. Returns the newly created record in a RecordSet. You can use the returned record to, ie, discover the values in auto-enter fields (like serial numbers).

For example:

result = myLayout.create({"First Name" => "Jerry", "Last Name" => "Robin"})
id = result[0]["ID"]

The above code adds a new record with first name Jerry and last name Robin. It then puts the value from the ID field (a serial number) into a ruby variable called id.



268
269
270
# File 'lib/rfm/layout.rb', line 268

def create(values, options = {})
  get_records('-new', values, options)
end

#databaseObject

alias_method :database, :db # This fails if db object hasn’t been set yet with meta_attr_accessor



132
133
134
# File 'lib/rfm/layout.rb', line 132

def database
  db
end

#dbObject



195
196
197
# File 'lib/rfm/layout.rb', line 195

def db
  db_orig || (self.db = Rfm::Database.new(state[:database], state[:account_name], state[:password], self))
end

#db_origObject



194
# File 'lib/rfm/layout.rb', line 194

alias_method :db_orig, :db

#delete(recid, options = {}) ⇒ Object

Deletes the record with the specified internal recid. Returns a ResultSet with the deleted record.

For example:

recid = myLayout.find({"First Name" => "Bill"})[0].record_id
myLayout.delete(recid)

The above code finds every record with Bill in the First Name field, then deletes the first one.



280
281
282
283
# File 'lib/rfm/layout.rb', line 280

def delete(recid, options = {})
  get_records('-delete', {'-recid' => recid}, options)
  return nil
end

#edit(recid, values, options = {}) ⇒ Object

Updates the contents of the record whose internal recid is specified. Send in a hash of new data in the values parameter. Returns a RecordSet containing the modified record. For example:

recid = myLayout.find({"First Name" => "Bill"})[0].record_id
myLayout.edit(recid, {"First Name" => "Steve"})

The above code would find the first record with Bill in the First Name field and change the first name to Steve.



252
253
254
255
# File 'lib/rfm/layout.rb', line 252

def edit(recid, values, options = {})
  get_records('-edit', {'-recid' => recid}.merge(values), options)
  #get_records('-edit', {'-recid' => recid}.merge(expand_repeats(values)), options) # attempt to set repeating fields.
end

#field_keysObject



368
369
370
371
372
373
374
375
# File 'lib/rfm/layout.rb', line 368

def field_keys
  # case
  # when @loaded; @meta.field_keys
  # when @resultset_meta; @resultset_meta.field_keys
  # else meta.field_keys
  # end
  meta.field_keys
end

#field_namesObject



349
350
351
352
353
354
355
356
357
# File 'lib/rfm/layout.rb', line 349

def field_names
  case
  when @loaded
    meta.field_names
  when @resultset_meta
    resultset_meta.field_names
  else meta.field_names
  end
end

#find(find_criteria, options = {}) ⇒ Object

Finds a record. Typically you will pass in a hash of field names and values. For example:

myLayout.find({"First Name" => "Bill"})

Values in the hash work just like value in FileMaker’s Find mode. You can use any special symbols (+==+, ..., >, etc…).

Create a Filemaker ‘omit’ request by including an :omit key with a value of true.

myLayout.find :field1 => 'val1', :field2 => 'val2', :omit => true

Create multiple Filemaker find requests by passing an array of hashes to the #find method.

myLayout.find [{:field1 => 'bill', :field2 => 'admin'}, {:field3 => 'inactive', :omit => true}, ...]

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.

myLayout.find :fieldOne => ['bill','mike','bob'], :fieldTwo =>'staff'

If you pass anything other than a hash or an array as the first parameter, it is converted to a string and assumed to be FileMaker’s internal id for a record (the recid).

myLayout.find 54321


233
234
235
236
237
# File 'lib/rfm/layout.rb', line 233

def find(find_criteria, options = {})
  #puts "layout.find-#{self.object_id}"
  options.merge!({:field_mapping => field_mapping.invert}) if field_mapping
  get_records(*Rfm::CompoundQuery.new(find_criteria, options))
end

#load_field_mapping(mapping = {}) ⇒ Object



434
435
436
437
438
439
440
# File 'lib/rfm/layout.rb', line 434

def load_field_mapping(mapping={})
  mapping = (mapping || {}).to_cih
  def mapping.invert
    super.to_cih
  end
  mapping
end

#metaObject

Metadata from Layout ###



345
346
347
# File 'lib/rfm/layout.rb', line 345

def meta
  @loaded ? @meta : load_layout
end

#modelizeObject

Creates new class with layout name.



443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# File 'lib/rfm/layout.rb', line 443

def modelize
  @model ||= (
    model_name = name.to_s.gsub(/\W|_/, ' ').title_case.gsub(/\s/,'')
    #model_class = eval("::" + model_name + "= Class.new(Rfm::Base)")
    model_class = Rfm.const_defined?(model_name) ? Rfm.const_get(model_name) : Rfm.const_set(model_name, Class.new(Rfm::Base))
    model_class.class_exec(self) do |layout_obj|
      @layout = layout_obj
    end
    model_class.config :parent=>'@layout'
    model_class
  )
  #     rescue StandardError, SyntaxError
  #       puts "Error in layout#modelize: #{$!}"
  #       nil
end

#modelsObject



459
460
461
462
# File 'lib/rfm/layout.rb', line 459

def models
  #subs.collect{|s| s.model}
  [@model]
end

#nameObject



338
339
340
# File 'lib/rfm/layout.rb', line 338

def name
  state[:layout].to_s
end

#portal_namesObject



402
403
404
# File 'lib/rfm/layout.rb', line 402

def portal_names
  return 'UNDER-CONTSTRUCTION'
end

#query(query_hash, options = {}) ⇒ Object

Access to raw -findquery command.



240
241
242
# File 'lib/rfm/layout.rb', line 240

def query(query_hash, options = {})
  get_records('-findquery', query_hash, options)
end

#resultset_meta_valid?Boolean

Returns:

  • (Boolean)


386
387
388
389
390
# File 'lib/rfm/layout.rb', line 386

def resultset_meta_valid?
  if @resultset_meta && @resultset_meta.error != '401'
    true
  end
end

#total_countObject

Should always refresh



393
394
395
# File 'lib/rfm/layout.rb', line 393

def total_count
  view.total_count
end

#view(options = {}) ⇒ Object

Retrieves metadata only, with an empty resultset.



286
287
288
# File 'lib/rfm/layout.rb', line 286

def view(options = {})
  get_records('-view', {}, options)
end