lardawge-rfm 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rfm/{commands/database.rb → database.rb} +0 -0
- data/lib/rfm/error.rb +146 -216
- data/lib/rfm/{commands/layout.rb → layout.rb} +3 -56
- data/lib/rfm/metadata/field.rb +93 -0
- data/lib/rfm/metadata/script.rb +20 -0
- data/lib/rfm/record.rb +219 -0
- data/lib/rfm/resultset.rb +136 -0
- data/lib/rfm/{commands/server.rb → server.rb} +10 -11
- data/lib/rfm/utilities/case_insensitive_hash.rb +10 -0
- data/lib/rfm/{factory.rb → utilities/factory.rb} +6 -6
- data/lib/rfm.rb +12 -234
- data/spec/rfm/error_spec.rb +69 -0
- data/spec/spec_helper.rb +9 -0
- metadata +40 -20
- data/lib/rfm/commands/field_control.rb +0 -50
- data/lib/rfm/commands/script.rb +0 -18
- data/lib/rfm/result.rb +0 -446
- data/lib/rfm/utility.rb +0 -12
- data/test/errors_test.rb +0 -53
data/lib/rfm/record.rb
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
module Rfm
|
2
|
+
|
3
|
+
# The Record object represents a single FileMaker record. You typically get them from ResultSet objects.
|
4
|
+
# For example, you might use a Layout object to find some records:
|
5
|
+
#
|
6
|
+
# results = myLayout.find({"First Name" => "Bill"})
|
7
|
+
#
|
8
|
+
# The +results+ variable in this example now contains a ResultSet object. ResultSets are really just arrays of
|
9
|
+
# Record objects (with a little extra added in). So you can get a record object just like you would access any
|
10
|
+
# typical array element:
|
11
|
+
#
|
12
|
+
# first_record = results[0]
|
13
|
+
#
|
14
|
+
# You can find out how many record were returned:
|
15
|
+
#
|
16
|
+
# record_count = results.size
|
17
|
+
#
|
18
|
+
# And you can of course iterate:
|
19
|
+
#
|
20
|
+
# results.each (|record|
|
21
|
+
# // you can work with the record here
|
22
|
+
# )
|
23
|
+
#
|
24
|
+
# =Accessing Field Data
|
25
|
+
#
|
26
|
+
# You can access field data in the Record object in two ways. Typically, you simply treat Record like a hash
|
27
|
+
# (because it _is_ a hash...I love OOP). Keys are field names:
|
28
|
+
#
|
29
|
+
# first = myRecord["First Name"]
|
30
|
+
# last = myRecord["Last Name"]
|
31
|
+
#
|
32
|
+
# If your field naming conventions mean that your field names are also valid Ruby symbol named (ie: they contain only
|
33
|
+
# letters, numbers, and underscores) then you can treat them like attributes of the record. For example, if your fields
|
34
|
+
# are called "first_name" and "last_name" you can do this:
|
35
|
+
#
|
36
|
+
# first = myRecord.first_name
|
37
|
+
# last = myRecord.last_name
|
38
|
+
#
|
39
|
+
# Note: This shortcut will fail (in a rather mysterious way) if your field name happens to match any real attribute
|
40
|
+
# name of a Record object. For instance, you may have a field called "server". If you try this:
|
41
|
+
#
|
42
|
+
# server_name = myRecord.server
|
43
|
+
#
|
44
|
+
# you'll actually set +server_name+ to the Rfm::Server object this Record came from. This won't fail until you try
|
45
|
+
# to treat it as a String somewhere else in your code. It is also possible a future version of Rfm will include
|
46
|
+
# new attributes on the Record class which may clash with your field names. This will cause perfectly valid code
|
47
|
+
# today to fail later when you upgrade. If you can't stomach this kind of insanity, stick with the hash-like
|
48
|
+
# method of field access, which has none of these limitations. Also note that the +myRecord[]+ method is probably
|
49
|
+
# somewhat faster since it doesn't go through +method_missing+.
|
50
|
+
#
|
51
|
+
# =Accessing Repeating Fields
|
52
|
+
#
|
53
|
+
# If you have a repeating field, RFM simply returns an array:
|
54
|
+
#
|
55
|
+
# val1 = myRecord["Price"][0]
|
56
|
+
# val2 = myRecord["Price"][1]
|
57
|
+
#
|
58
|
+
# In the above example, the Price field is a repeating field. The code puts the first repetition in a variable called
|
59
|
+
# +val1+ and the second in a variable called +val2+.
|
60
|
+
#
|
61
|
+
# =Accessing Portals
|
62
|
+
#
|
63
|
+
# If the ResultSet includes portals (because the layout it comes from has portals on it) you can access them
|
64
|
+
# using the Record::portals attribute. It is a hash with table occurrence names for keys, and arrays of Record
|
65
|
+
# objects for values. In other words, you can do this:
|
66
|
+
#
|
67
|
+
# myRecord.portals["Orders"].each {|record|
|
68
|
+
# puts record["Order Number"]
|
69
|
+
# }
|
70
|
+
#
|
71
|
+
# This code iterates through the rows of the _Orders_ portal.
|
72
|
+
#
|
73
|
+
# =Field Types and Ruby Types
|
74
|
+
#
|
75
|
+
# RFM automatically converts data from FileMaker into a Ruby object with the most reasonable type possible. The
|
76
|
+
# type are mapped thusly:
|
77
|
+
#
|
78
|
+
# * *Text* fields are converted to Ruby String objects
|
79
|
+
#
|
80
|
+
# * *Number* fields are converted to Ruby BigDecimal objects (the basic Ruby numeric types have
|
81
|
+
# much less precision and range than FileMaker number fields)
|
82
|
+
#
|
83
|
+
# * *Date* fields are converted to Ruby Date objects
|
84
|
+
#
|
85
|
+
# * *Time* fields are converted to Ruby DateTime objects (you can ignore the date component)
|
86
|
+
#
|
87
|
+
# * *Timestamp* fields are converted to Ruby DateTime objects
|
88
|
+
#
|
89
|
+
# * *Container* fields are converted to Ruby URI objects
|
90
|
+
#
|
91
|
+
# =Attributes
|
92
|
+
#
|
93
|
+
# In addition to +portals+, the Record object has these useful attributes:
|
94
|
+
#
|
95
|
+
# * *record_id* is FileMaker's internal identifier for this record (_not_ any ID field you might have
|
96
|
+
# in your table); you need a +record_id+ to edit or delete a record
|
97
|
+
#
|
98
|
+
# * *mod_id* is the modification identifier for the record; whenever a record is modified, its +mod_id+
|
99
|
+
# changes so you can tell if the Record object you're looking at is up-to-date as compared to another
|
100
|
+
# copy of the same record
|
101
|
+
class Record < Rfm::CaseInsensitiveHash
|
102
|
+
|
103
|
+
attr_reader :record_id, :mod_id, :portals
|
104
|
+
|
105
|
+
def initialize(record, result, field_meta, layout, portal=nil)
|
106
|
+
@record_id = record['record-id']
|
107
|
+
@mod_id = record['mod-id']
|
108
|
+
@mods = {}
|
109
|
+
@layout = layout
|
110
|
+
@portals ||= Rfm::CaseInsensitiveHash.new
|
111
|
+
|
112
|
+
relatedsets = !portal && result.instance_variable_get(:@include_portals) ? record.xpath('relatedset') : []
|
113
|
+
|
114
|
+
record.xpath('field').each do |field|
|
115
|
+
field_name = field['name']
|
116
|
+
field_name.gsub!(Regexp.new(portal + '::'), '') if portal
|
117
|
+
datum = []
|
118
|
+
|
119
|
+
field.xpath('data').each do |x|
|
120
|
+
datum.push(field_meta[field_name].coerce(x.inner_text, result))
|
121
|
+
end
|
122
|
+
|
123
|
+
if datum.length == 1
|
124
|
+
self[field_name] = datum[0]
|
125
|
+
elsif datum.length == 0
|
126
|
+
self[field_name] = nil
|
127
|
+
else
|
128
|
+
self[field_name] = datum
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
unless relatedsets.empty?
|
133
|
+
relatedsets.each do |relatedset|
|
134
|
+
tablename, records = relatedset['table'], []
|
135
|
+
|
136
|
+
relatedset.xpath('record').each do |record|
|
137
|
+
records << self.class.new(record, result, result.portal_meta[tablename], layout, tablename)
|
138
|
+
end
|
139
|
+
|
140
|
+
@portals[tablename] = records
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
@loaded = true
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.build_records(records, result, field_meta, layout, portal=nil)
|
148
|
+
records.each do |record|
|
149
|
+
result << self.new(record, result, field_meta, layout, portal)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Saves local changes to the Record object back to Filemaker. For example:
|
154
|
+
#
|
155
|
+
# myLayout.find({"First Name" => "Bill"}).each(|record|
|
156
|
+
# record["First Name"] = "Steve"
|
157
|
+
# record.save
|
158
|
+
# )
|
159
|
+
#
|
160
|
+
# This code finds every record with _Bill_ in the First Name field, then changes the first name to
|
161
|
+
# Steve.
|
162
|
+
#
|
163
|
+
# Note: This method is smart enough to not bother saving if nothing has changed. So there's no need
|
164
|
+
# to optimize on your end. Just save, and if you've changed the record it will be saved. If not, no
|
165
|
+
# server hit is incurred.
|
166
|
+
def save
|
167
|
+
self.merge(@layout.edit(self.record_id, @mods)[0]) if @mods.size > 0
|
168
|
+
@mods.clear
|
169
|
+
end
|
170
|
+
|
171
|
+
# Like Record::save, except it fails (and raises an error) if the underlying record in FileMaker was
|
172
|
+
# modified after the record was fetched but before it was saved. In other words, prevents you from
|
173
|
+
# accidentally overwriting changes someone else made to the record.
|
174
|
+
def save_if_not_modified
|
175
|
+
self.merge(@layout.edit(@record_id, @mods, {:modification_id => @mod_id})[0]) if @mods.size > 0
|
176
|
+
@mods.clear
|
177
|
+
end
|
178
|
+
|
179
|
+
# Gets the value of a field from the record. For example:
|
180
|
+
#
|
181
|
+
# first = myRecord["First Name"]
|
182
|
+
# last = myRecord["Last Name"]
|
183
|
+
#
|
184
|
+
# This sample puts the first and last name from the record into Ruby variables.
|
185
|
+
#
|
186
|
+
# You can also update a field:
|
187
|
+
#
|
188
|
+
# myRecord["First Name"] = "Sophia"
|
189
|
+
#
|
190
|
+
# When you do, the change is noted, but *the data is not updated in FileMaker*. You must call
|
191
|
+
# Record::save or Record::save_if_not_modified to actually save the data.
|
192
|
+
def []=(pname, value)
|
193
|
+
return super unless @loaded # keeps us from getting mods during initialization
|
194
|
+
name = pname
|
195
|
+
if self[name] != nil
|
196
|
+
@mods[name] = val
|
197
|
+
else
|
198
|
+
raise Rfm::Error::ParameterError.new("You attempted to modify a field called '#{name}' on the Rfm::Record object, but that field does not exist.")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def method_missing (symbol, *attrs)
|
203
|
+
# check for simple getter
|
204
|
+
return self[symbol.to_s] if self.include?(symbol.to_s)
|
205
|
+
|
206
|
+
# check for setter
|
207
|
+
symbol_name = symbol.to_s
|
208
|
+
if symbol_name[-1..-1] == '=' && self.has_key?(symbol_name[0..-2])
|
209
|
+
return @mods[symbol_name[0..-2]] = attrs[0]
|
210
|
+
end
|
211
|
+
super
|
212
|
+
end
|
213
|
+
|
214
|
+
def respond_to?(symbol, include_private = false)
|
215
|
+
return true if self[symbol.to_s] != nil
|
216
|
+
super
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# This module includes classes that represent FileMaker data. When you communicate with FileMaker
|
2
|
+
# using, ie, the Layout object, you typically get back ResultSet objects. These contain Records,
|
3
|
+
# which in turn contain Fields, Portals, and arrays of data.
|
4
|
+
#
|
5
|
+
# Author:: Geoff Coffey (mailto:gwcoffey@gmail.com)
|
6
|
+
# Copyright:: Copyright (c) 2007 Six Fried Rice, LLC and Mufaddal Khumri
|
7
|
+
# License:: See MIT-LICENSE for details
|
8
|
+
require 'nokogiri'
|
9
|
+
require 'bigdecimal'
|
10
|
+
require 'rfm/record'
|
11
|
+
require 'rfm/metadata/field'
|
12
|
+
|
13
|
+
module Rfm
|
14
|
+
|
15
|
+
# The ResultSet object represents a set of records in FileMaker. It is, in every way, a real Ruby
|
16
|
+
# Array, so everything you expect to be able to do with an Array can be done with a ResultSet as well.
|
17
|
+
# In this case, the elements in the array are Record objects.
|
18
|
+
#
|
19
|
+
# Here's a typical example, displaying the results of a Find:
|
20
|
+
#
|
21
|
+
# myServer = Rfm::Server.new(...)
|
22
|
+
# results = myServer["Customers"]["Details"].find("First Name" => "Bill")
|
23
|
+
# results.each {|record|
|
24
|
+
# puts record["First Name"]
|
25
|
+
# puts record["Last Name"]
|
26
|
+
# puts record["Email Address"]
|
27
|
+
# }
|
28
|
+
#
|
29
|
+
# =Attributes
|
30
|
+
#
|
31
|
+
# The ResultSet object has these attributes:
|
32
|
+
#
|
33
|
+
# * *field_meta* is a hash with field names for keys and Field objects for values; it provides
|
34
|
+
# info about the fields in the ResultSet
|
35
|
+
#
|
36
|
+
# * *portal_meta* is a hash with table occurrence names for keys and arrays of Field objects for values;
|
37
|
+
# it provides metadata about the portals in the ResultSet and the Fields on those portals
|
38
|
+
|
39
|
+
class Resultset < Array
|
40
|
+
|
41
|
+
attr_reader :layout
|
42
|
+
attr_reader :field_meta, :portal_meta
|
43
|
+
attr_reader :date_format, :time_format, :timestamp_format
|
44
|
+
attr_reader :total_count, :foundset_count
|
45
|
+
|
46
|
+
# Initializes a new ResultSet object. You will probably never do this your self (instead, use the Layout
|
47
|
+
# object to get various ResultSet obejects).
|
48
|
+
#
|
49
|
+
# If you feel so inclined, though, pass a Server object, and some +fmpxmlresult+ compliant XML in a String.
|
50
|
+
#
|
51
|
+
# =Attributes
|
52
|
+
#
|
53
|
+
# The ResultSet object includes several useful attributes:
|
54
|
+
#
|
55
|
+
# * *fields* is a hash (with field names for keys and Field objects for values). It includes an entry for
|
56
|
+
# every field in the ResultSet. Note: You don't use Field objects to access _data_. If you're after
|
57
|
+
# data, get a Record object (ResultSet is an array of records). Field objects tell you about the fields
|
58
|
+
# (their type, repetitions, and so forth) in case you find that information useful programmatically.
|
59
|
+
#
|
60
|
+
# Note: keys in the +fields+ hash are downcased for convenience (and [] automatically downcases on
|
61
|
+
# lookup, so it should be seamless). But if you +each+ a field hash and need to know a field's real
|
62
|
+
# name, with correct case, do +myField.name+ instead of relying on the key in the hash.
|
63
|
+
#
|
64
|
+
# * *portals* is a hash (with table occurrence names for keys and Field objects for values). If your
|
65
|
+
# layout contains portals, you can find out what fields they contain here. Again, if it's the data you're
|
66
|
+
# after, you want to look at the Record object.
|
67
|
+
|
68
|
+
def initialize(server, xml_response, layout, portals=nil)
|
69
|
+
@layout = layout
|
70
|
+
@field_meta ||= Rfm::CaseInsensitiveHash.new
|
71
|
+
@portal_meta ||= Rfm::CaseInsensitiveHash.new
|
72
|
+
@include_portals = portals
|
73
|
+
|
74
|
+
doc = Nokogiri.XML(remove_namespace(xml_response))
|
75
|
+
|
76
|
+
error = doc.xpath('/fmresultset/error').attribute('code').value.to_i
|
77
|
+
check_for_errors(error, server.state[:raise_on_401])
|
78
|
+
|
79
|
+
datasource = doc.xpath('/fmresultset/datasource')
|
80
|
+
meta = doc.xpath('/fmresultset/metadata')
|
81
|
+
resultset = doc.xpath('/fmresultset/resultset')
|
82
|
+
|
83
|
+
@date_format = convert_date_time_format(datasource.attribute('date-format').value)
|
84
|
+
@time_format = convert_date_time_format(datasource.attribute('time-format').value)
|
85
|
+
@timestamp_format = convert_date_time_format(datasource.attribute('timestamp-format').value)
|
86
|
+
|
87
|
+
@foundset_count = resultset.attribute('count').value.to_i
|
88
|
+
@total_count = datasource.attribute('total-count').value.to_i
|
89
|
+
|
90
|
+
parse_fields(meta)
|
91
|
+
parse_portals(meta) if @include_portals
|
92
|
+
|
93
|
+
Rfm::Record.build_records(resultset.xpath('record'), self, @field_meta, @layout)
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def remove_namespace(xml)
|
99
|
+
xml.gsub('xmlns="http://www.filemaker.com/xml/fmresultset" version="1.0"', '')
|
100
|
+
end
|
101
|
+
|
102
|
+
def check_for_errors(code, raise_401)
|
103
|
+
raise Rfm::Error.getError(code) if code != 0 && (code != 401 || raise_401)
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_fields(meta)
|
107
|
+
meta.xpath('field-definition').each do |field|
|
108
|
+
@field_meta[field['name']] = Rfm::Metadata::Field.new(field)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse_portals(meta)
|
113
|
+
meta.xpath('relatedset-definition').each do |relatedset|
|
114
|
+
table, fields = relatedset.attribute('table').value, {}
|
115
|
+
|
116
|
+
relatedset.xpath('field-definition').each do |field|
|
117
|
+
name = field.attribute('name').value.gsub(Regexp.new(table + '::'), '')
|
118
|
+
fields[name] = Rfm::Metadata::Field.new(field)
|
119
|
+
end
|
120
|
+
|
121
|
+
@portal_meta[table] = fields
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def convert_date_time_format(fm_format)
|
126
|
+
fm_format.gsub!('MM', '%m')
|
127
|
+
fm_format.gsub!('dd', '%d')
|
128
|
+
fm_format.gsub!('yyyy', '%Y')
|
129
|
+
fm_format.gsub!('HH', '%H')
|
130
|
+
fm_format.gsub!('mm', '%M')
|
131
|
+
fm_format.gsub!('ss', '%S')
|
132
|
+
fm_format
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -254,7 +254,7 @@ module Rfm
|
|
254
254
|
# For example, if you wanted to send a raw command to FileMaker to find the first 20 people in the
|
255
255
|
# "Customers" database whose first name is "Bill" you might do this:
|
256
256
|
#
|
257
|
-
# response = myServer.
|
257
|
+
# response = myServer.connect(
|
258
258
|
# '-find',
|
259
259
|
# {
|
260
260
|
# "-db" => "Customers",
|
@@ -263,7 +263,7 @@ module Rfm
|
|
263
263
|
# },
|
264
264
|
# { :max_records => 20 }
|
265
265
|
# )
|
266
|
-
def
|
266
|
+
def connect(account_name, password, action, args, options = {})
|
267
267
|
post = args.merge(expand_options(options)).merge({action => ''})
|
268
268
|
http_fetch(@host_name, @port, "/fmi/xml/fmresultset.xml", account_name, password, post)
|
269
269
|
end
|
@@ -276,7 +276,7 @@ module Rfm
|
|
276
276
|
private
|
277
277
|
|
278
278
|
def http_fetch(host_name, port, path, account_name, password, post_data, limit=10)
|
279
|
-
raise Rfm::
|
279
|
+
raise Rfm::CommunicationError.new("While trying to reach the Web Publishing Engine, RFM was redirected too many times.") if limit == 0
|
280
280
|
|
281
281
|
if @state[:log_actions] == true
|
282
282
|
qs = post_data.collect{|key,val| "#{CGI::escape(key.to_s)}=#{CGI::escape(val.to_s)}"}.join("&")
|
@@ -300,7 +300,6 @@ module Rfm
|
|
300
300
|
end
|
301
301
|
|
302
302
|
response = response.start { |http| http.request(request) }
|
303
|
-
|
304
303
|
if @state[:log_responses] == true
|
305
304
|
response.to_hash.each { |key, value| warn "#{key}: #{value}" }
|
306
305
|
warn response.body
|
@@ -318,13 +317,13 @@ module Rfm
|
|
318
317
|
http_fetch(newloc.host, newloc.port, newloc.request_uri, account_name, password, post_data, limit - 1)
|
319
318
|
when Net::HTTPUnauthorized
|
320
319
|
msg = "The account name (#{account_name}) or password provided is not correct (or the account doesn't have the fmxml extended privilege)."
|
321
|
-
raise Rfm::
|
320
|
+
raise Rfm::AuthenticationError.new(msg)
|
322
321
|
when Net::HTTPNotFound
|
323
322
|
msg = "Could not talk to FileMaker because the Web Publishing Engine is not responding (server returned 404)."
|
324
|
-
raise Rfm::
|
323
|
+
raise Rfm::CommunicationError.new(msg)
|
325
324
|
else
|
326
|
-
msg = "Unexpected response from server: #{
|
327
|
-
raise Rfm::
|
325
|
+
msg = "Unexpected response from server: #{response.code} (#{response.class.to_s}). Unable to communicate with the Web Publishing Engine."
|
326
|
+
raise Rfm::CommunicationError.new(msg)
|
328
327
|
end
|
329
328
|
end
|
330
329
|
|
@@ -338,14 +337,14 @@ module Rfm
|
|
338
337
|
result['-skip'] = value
|
339
338
|
when :sort_field
|
340
339
|
if value.kind_of? Array
|
341
|
-
raise Rfm::
|
340
|
+
raise Rfm::ParameterError.new(":sort_field can have at most 9 fields, but you passed an array with #{value.size} elements.") if value.size > 9
|
342
341
|
value.each_index { |i| result["-sortfield.#{i+1}"] = value[i] }
|
343
342
|
else
|
344
343
|
result["-sortfield.1"] = value
|
345
344
|
end
|
346
345
|
when :sort_order
|
347
346
|
if value.kind_of? Array
|
348
|
-
raise Rfm::
|
347
|
+
raise Rfm::ParameterError.new(":sort_order can have at most 9 fields, but you passed an array with #{value.size} elements.") if value.size > 9
|
349
348
|
value.each_index { |i| result["-sortorder.#{i+1}"] = value[i] }
|
350
349
|
else
|
351
350
|
result["-sortorder.1"] = value
|
@@ -378,7 +377,7 @@ module Rfm
|
|
378
377
|
when :modification_id
|
379
378
|
result['-modid'] = value
|
380
379
|
else
|
381
|
-
raise Rfm::
|
380
|
+
raise Rfm::ParameterError.new("Invalid option: #{key} (are you using a string instead of a symbol?)")
|
382
381
|
end
|
383
382
|
end
|
384
383
|
return result
|
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
module Rfm
|
9
9
|
module Factory # :nodoc: all
|
10
|
-
class DbFactory < Rfm::
|
10
|
+
class DbFactory < Rfm::CaseInsensitiveHash
|
11
11
|
|
12
12
|
def initialize(server)
|
13
13
|
@server = server
|
@@ -20,7 +20,7 @@ module Rfm
|
|
20
20
|
|
21
21
|
def all
|
22
22
|
if !@loaded
|
23
|
-
Rfm::Result::ResultSet.new(@server, @server.
|
23
|
+
Rfm::Result::ResultSet.new(@server, @server.connect(@server.state[:account_name], @server.state[:password], '-dbnames', {}).body).each {|record|
|
24
24
|
name = record['DATABASE_NAME']
|
25
25
|
self[name] = Rfm::Database.new(name, @server) if self[name] == nil
|
26
26
|
}
|
@@ -31,7 +31,7 @@ module Rfm
|
|
31
31
|
|
32
32
|
end
|
33
33
|
|
34
|
-
class LayoutFactory < Rfm::
|
34
|
+
class LayoutFactory < Rfm::CaseInsensitiveHash
|
35
35
|
|
36
36
|
def initialize(server, database)
|
37
37
|
@server = server
|
@@ -45,7 +45,7 @@ module Rfm
|
|
45
45
|
|
46
46
|
def all
|
47
47
|
if !@loaded
|
48
|
-
Rfm::Result::ResultSet.new(@server, @server.
|
48
|
+
Rfm::Result::ResultSet.new(@server, @server.connect(@server.state[:account_name], @server.state[:password], '-layoutnames', {"-db" => @database.name}).body).each {|record|
|
49
49
|
name = record['LAYOUT_NAME']
|
50
50
|
self[name] = Rfm::Layout.new(name, @database) if self[name] == nil
|
51
51
|
}
|
@@ -56,7 +56,7 @@ module Rfm
|
|
56
56
|
|
57
57
|
end
|
58
58
|
|
59
|
-
class ScriptFactory < Rfm::
|
59
|
+
class ScriptFactory < Rfm::CaseInsensitiveHash
|
60
60
|
|
61
61
|
def initialize(server, database)
|
62
62
|
@server = server
|
@@ -70,7 +70,7 @@ module Rfm
|
|
70
70
|
|
71
71
|
def all
|
72
72
|
if !@loaded
|
73
|
-
Rfm::Result::ResultSet.new(@server, @server.
|
73
|
+
Rfm::Result::ResultSet.new(@server, @server.connect(@server.state[:account_name], @server.state[:password], '-scriptnames', {"-db" => @database.name}).body).each {|record|
|
74
74
|
name = record['SCRIPT_NAME']
|
75
75
|
self[name] = Rfm::Script.new(name, @database) if self[name] == nil
|
76
76
|
}
|