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.
- checksums.yaml +15 -0
- data/CHANGELOG.md +45 -16
- data/README.md +251 -274
- data/lib/rfm.rb +42 -20
- data/lib/rfm/VERSION +1 -1
- data/lib/rfm/base.rb +63 -196
- data/lib/rfm/database.rb +15 -16
- data/lib/rfm/layout.rb +244 -271
- data/lib/rfm/metadata/datum.rb +45 -0
- data/lib/rfm/metadata/field.rb +33 -13
- data/lib/rfm/metadata/field_control.rb +57 -25
- data/lib/rfm/metadata/layout_meta.rb +38 -0
- data/lib/rfm/metadata/resultset_meta.rb +66 -0
- data/lib/rfm/metadata/value_list_item.rb +7 -6
- data/lib/rfm/record.rb +54 -74
- data/lib/rfm/resultset.rb +63 -112
- data/lib/rfm/server.rb +6 -172
- data/lib/rfm/utilities/config.rb +100 -55
- data/lib/rfm/utilities/connection.rb +209 -0
- data/lib/rfm/utilities/core_ext.rb +14 -1
- data/lib/rfm/utilities/factory.rb +68 -65
- data/lib/rfm/utilities/sax_parser.rb +1039 -0
- metadata +154 -206
- data/lib/rfm/utilities/fmpxmlresult.rb +0 -167
- data/lib/rfm/utilities/fmresultset.rb +0 -153
- data/lib/rfm/utilities/xml_parser.rb +0 -124
- data/lib/rfm/xml_mini/hpricot.rb +0 -133
- data/lib/rfm/xml_mini/ox_sax.rb +0 -91
- data/lib/rfm/xml_mini/rexml_sax.rb +0 -81
data/lib/rfm/database.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
117
|
+
def_delegators :layouts, :[], :modelize, :models # modelize & models acquired from Rfm::Base
|
119
118
|
|
120
119
|
end
|
121
120
|
end
|
data/lib/rfm/layout.rb
CHANGED
@@ -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
|
-
|
139
|
-
|
140
|
-
|
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
|
-
@
|
150
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
345
|
-
|
346
|
-
|
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
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
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
|
-
|
359
|
-
|
360
|
-
|
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
|
-
|
368
|
-
|
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
|
372
|
-
|
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
|
-
|
368
|
+
return 'UNDER-CONTSTRUCTION'
|
381
369
|
end
|
370
|
+
|
382
371
|
|
383
|
-
def table
|
384
|
-
@table ||= view.table
|
385
|
-
end
|
386
372
|
|
387
|
-
|
388
|
-
|
389
|
-
end
|
373
|
+
|
374
|
+
### Utility ###
|
390
375
|
|
391
|
-
def
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
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(
|
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 :
|
420
|
+
private :load_layout, :get_records, :params
|
448
421
|
|
449
422
|
|
450
423
|
end # Layout
|