lardawge-rfm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +233 -0
- data/lib/rfm.rb +232 -0
- data/lib/rfm_command.rb +713 -0
- data/lib/rfm_error.rb +252 -0
- data/lib/rfm_factory.rb +82 -0
- data/lib/rfm_result.rb +430 -0
- data/lib/rfm_utility.rb +10 -0
- data/test/rfm_test_errors.rb +52 -0
- data/test/rfm_tester.rb +2 -0
- metadata +72 -0
data/lib/rfm_result.rb
ADDED
@@ -0,0 +1,430 @@
|
|
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 'bigdecimal'
|
9
|
+
require 'date'
|
10
|
+
|
11
|
+
module Rfm::Result
|
12
|
+
|
13
|
+
# The ResultSet object represents a set of records in FileMaker. It is, in every way, a real Ruby
|
14
|
+
# Array, so everything you expect to be able to do with an Array can be done with a ResultSet as well.
|
15
|
+
# In this case, the elements in the array are Record objects.
|
16
|
+
#
|
17
|
+
# Here's a typical example, displaying the results of a Find:
|
18
|
+
#
|
19
|
+
# myServer = Rfm::Server.new(...)
|
20
|
+
# results = myServer["Customers"]["Details"].find("First Name" => "Bill")
|
21
|
+
# results.each {|record|
|
22
|
+
# puts record["First Name"]
|
23
|
+
# puts record["Last Name"]
|
24
|
+
# puts record["Email Address"]
|
25
|
+
# }
|
26
|
+
#
|
27
|
+
# =Attributes
|
28
|
+
#
|
29
|
+
# The ResultSet object has several useful attributes:
|
30
|
+
#
|
31
|
+
# * *server* is the server object this ResultSet came from
|
32
|
+
#
|
33
|
+
# * *fields* is a hash with field names for keys and Field objects for values; it provides
|
34
|
+
# metadata about the fields in the ResultSet
|
35
|
+
#
|
36
|
+
# * *portals* 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
|
+
# Initializes a new ResultSet object. You will probably never do this your self (instead, use the Layout
|
42
|
+
# object to get various ResultSet obejects).
|
43
|
+
#
|
44
|
+
# If you feel so inclined, though, pass a Server object, and some +fmpxmlresult+ compliant XML in a String.
|
45
|
+
#
|
46
|
+
# =Attributes
|
47
|
+
#
|
48
|
+
# The ResultSet object includes several useful attributes:
|
49
|
+
#
|
50
|
+
# * *fields* is a hash (with field names for keys and Field objects for values). It includes an entry for
|
51
|
+
# every field in the ResultSet. Note: You don't use Field objects to access _data_. If you're after
|
52
|
+
# data, get a Record object (ResultSet is an array of records). Field objects tell you about the fields
|
53
|
+
# (their type, repetitions, and so forth) in case you find that information useful programmatically.
|
54
|
+
#
|
55
|
+
# Note: keys in the +fields+ hash are downcased for convenience (and [] automatically downcases on
|
56
|
+
# lookup, so it should be seamless). But if you +each+ a field hash and need to know a field's real
|
57
|
+
# name, with correct case, do +myField.name+ instead of relying on the key in the hash.
|
58
|
+
#
|
59
|
+
# * *portals* is a hash (with table occurrence names for keys and Field objects for values). If your
|
60
|
+
# layout contains portals, you can find out what fields they contain here. Again, if it's the data you're
|
61
|
+
# after, you want to look at the Record object.
|
62
|
+
def initialize(server, fmresultset, layout = nil)
|
63
|
+
@server = server
|
64
|
+
@resultset = nil
|
65
|
+
@layout = layout
|
66
|
+
@fields = Rfm::Util::CaseInsensitiveHash.new
|
67
|
+
@portals = Rfm::Util::CaseInsensitiveHash.new
|
68
|
+
@date_format = nil
|
69
|
+
@time_format = nil
|
70
|
+
@timestamp_format = nil
|
71
|
+
|
72
|
+
doc = REXML::Document.new(fmresultset)
|
73
|
+
root = doc.root
|
74
|
+
|
75
|
+
# check for errors
|
76
|
+
error = root.elements['error'].attributes['code'].to_i
|
77
|
+
if error != 0 && (error != 401 || @server.state[:raise_on_401])
|
78
|
+
raise Rfm::Error::FileMakerError.getError(error)
|
79
|
+
end
|
80
|
+
|
81
|
+
# ascertain date and time formats
|
82
|
+
datasource = root.elements['datasource']
|
83
|
+
@date_format = convertFormatString(datasource.attributes['date-format'])
|
84
|
+
@time_format = convertFormatString(datasource.attributes['time-format'])
|
85
|
+
@timestamp_format = convertFormatString(datasource.attributes['timestamp-format'])
|
86
|
+
|
87
|
+
# process field metadata
|
88
|
+
root.elements['metadata'].each_element('field-definition') { |field|
|
89
|
+
name = field.attributes['name']
|
90
|
+
@fields[name] = Field.new(self, field)
|
91
|
+
}
|
92
|
+
@fields.freeze
|
93
|
+
|
94
|
+
# process relatedset metadata
|
95
|
+
root.elements['metadata'].each_element('relatedset-definition') { |relatedset|
|
96
|
+
table = relatedset.attributes['table']
|
97
|
+
fields = {}
|
98
|
+
relatedset.each_element('field-definition') { |field|
|
99
|
+
name = field.attributes['name'].sub(Regexp.new(table + '::'), '')
|
100
|
+
fields[name] = Field.new(self, field)
|
101
|
+
}
|
102
|
+
@portals[table] = fields
|
103
|
+
}
|
104
|
+
@portals.freeze
|
105
|
+
|
106
|
+
# build rows
|
107
|
+
root.elements['resultset'].each_element('record') { |record|
|
108
|
+
self << Record.new(record, self, @fields, @layout)
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
attr_reader :server, :fields, :portals, :date_format, :time_format, :timestamp_format, :layout
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def convertFormatString(fm_format)
|
117
|
+
fm_format.gsub('MM', '%m').gsub('dd', '%d').gsub('yyyy', '%Y').gsub('HH', '%H').gsub('mm', '%M').gsub('ss', '%S')
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
# The Record object represents a single FileMaker record. You typically get them from ResultSet objects.
|
123
|
+
# For example, you might use a Layout object to find some records:
|
124
|
+
#
|
125
|
+
# results = myLayout.find({"First Name" => "Bill"})
|
126
|
+
#
|
127
|
+
# The +results+ variable in this example now contains a ResultSet object. ResultSets are really just arrays of
|
128
|
+
# Record objects (with a little extra added in). So you can get a record object just like you would access any
|
129
|
+
# typical array element:
|
130
|
+
#
|
131
|
+
# first_record = results[0]
|
132
|
+
#
|
133
|
+
# You can find out how many record were returned:
|
134
|
+
#
|
135
|
+
# record_count = results.size
|
136
|
+
#
|
137
|
+
# And you can of course iterate:
|
138
|
+
#
|
139
|
+
# results.each (|record|
|
140
|
+
# // you can work with the record here
|
141
|
+
# )
|
142
|
+
#
|
143
|
+
# =Accessing Field Data
|
144
|
+
#
|
145
|
+
# You can access field data in the Record object in two ways. Typically, you simply treat Record like a hash
|
146
|
+
# (because it _is_ a hash...I love OOP). Keys are field names:
|
147
|
+
#
|
148
|
+
# first = myRecord["First Name"]
|
149
|
+
# last = myRecord["Last Name"]
|
150
|
+
#
|
151
|
+
# If your field naming conventions mean that your field names are also valid Ruby symbol named (ie: they contain only
|
152
|
+
# letters, numbers, and underscores) then you can treat them like attributes of the record. For example, if your fields
|
153
|
+
# are called "first_name" and "last_name" you can do this:
|
154
|
+
#
|
155
|
+
# first = myRecord.first_name
|
156
|
+
# last = myRecord.last_name
|
157
|
+
#
|
158
|
+
# Note: This shortcut will fail (in a rather mysterious way) if your field name happens to match any real attribute
|
159
|
+
# name of a Record object. For instance, you may have a field called "server". If you try this:
|
160
|
+
#
|
161
|
+
# server_name = myRecord.server
|
162
|
+
#
|
163
|
+
# you'll actually set +server_name+ to the Rfm::Server object this Record came from. This won't fail until you try
|
164
|
+
# to treat it as a String somewhere else in your code. It is also possible a future version of Rfm will include
|
165
|
+
# new attributes on the Record class which may clash with your field names. This will cause perfectly valid code
|
166
|
+
# today to fail later when you upgrade. If you can't stomach this kind of insanity, stick with the hash-like
|
167
|
+
# method of field access, which has none of these limitations. Also note that the +myRecord[]+ method is probably
|
168
|
+
# somewhat faster since it doesn't go through +method_missing+.
|
169
|
+
#
|
170
|
+
# =Accessing Repeating Fields
|
171
|
+
#
|
172
|
+
# If you have a repeating field, RFM simply returns an array:
|
173
|
+
#
|
174
|
+
# val1 = myRecord["Price"][0]
|
175
|
+
# val2 = myRecord["Price"][1]
|
176
|
+
#
|
177
|
+
# In the above example, the Price field is a repeating field. The code puts the first repetition in a variable called
|
178
|
+
# +val1+ and the second in a variable called +val2+.
|
179
|
+
#
|
180
|
+
# =Accessing Portals
|
181
|
+
#
|
182
|
+
# If the ResultSet includes portals (because the layout it comes from has portals on it) you can access them
|
183
|
+
# using the Record::portals attribute. It is a hash with table occurrence names for keys, and arrays of Record
|
184
|
+
# objects for values. In other words, you can do this:
|
185
|
+
#
|
186
|
+
# myRecord.portals["Orders"].each {|record|
|
187
|
+
# puts record["Order Number"]
|
188
|
+
# }
|
189
|
+
#
|
190
|
+
# This code iterates through the rows of the _Orders_ portal.
|
191
|
+
#
|
192
|
+
# =Field Types and Ruby Types
|
193
|
+
#
|
194
|
+
# RFM automatically converts data from FileMaker into a Ruby object with the most reasonable type possible. The
|
195
|
+
# type are mapped thusly:
|
196
|
+
#
|
197
|
+
# * *Text* fields are converted to Ruby String objects
|
198
|
+
#
|
199
|
+
# * *Number* fields are converted to Ruby BigDecimal objects (the basic Ruby numeric types have
|
200
|
+
# much less precision and range than FileMaker number fields)
|
201
|
+
#
|
202
|
+
# * *Date* fields are converted to Ruby Date objects
|
203
|
+
#
|
204
|
+
# * *Time* fields are converted to Ruby DateTime objects (you can ignore the date component)
|
205
|
+
#
|
206
|
+
# * *Timestamp* fields are converted to Ruby DateTime objects
|
207
|
+
#
|
208
|
+
# * *Container* fields are converted to Ruby URI objects
|
209
|
+
#
|
210
|
+
# =Attributes
|
211
|
+
#
|
212
|
+
# In addition to +portals+, the Record object has these useful attributes:
|
213
|
+
#
|
214
|
+
# * *record_id* is FileMaker's internal identifier for this record (_not_ any ID field you might have
|
215
|
+
# in your table); you need a +record_id+ to edit or delete a record
|
216
|
+
#
|
217
|
+
# * *mod_id* is the modification identifier for the record; whenever a record is modified, its +mod_id+
|
218
|
+
# changes so you can tell if the Record object you're looking at is up-to-date as compared to another
|
219
|
+
# copy of the same record
|
220
|
+
class Record < Rfm::Util::CaseInsensitiveHash
|
221
|
+
|
222
|
+
# Initializes a Record object. You really really never need to do this yourself. Instead, get your records
|
223
|
+
# from a ResultSet object.
|
224
|
+
def initialize(row_element, resultset, fields, layout, portal=nil)
|
225
|
+
@record_id = row_element.attributes['record-id']
|
226
|
+
@mod_id = row_element.attributes['mod-id']
|
227
|
+
@mods = {}
|
228
|
+
@resultset = resultset
|
229
|
+
@layout = layout
|
230
|
+
|
231
|
+
@loaded = false
|
232
|
+
|
233
|
+
row_element.each_element('field') { |field|
|
234
|
+
field_name = field.attributes['name']
|
235
|
+
field_name.sub!(Regexp.new(portal + '::'), '') if portal
|
236
|
+
datum = []
|
237
|
+
field.each_element('data') {|x| datum.push(fields[field_name].coerce(x.text))}
|
238
|
+
if datum.length == 1
|
239
|
+
self[field_name] = datum[0]
|
240
|
+
elsif datum.length == 0
|
241
|
+
self[field_name] = nil
|
242
|
+
else
|
243
|
+
self[field_name] = datum
|
244
|
+
end
|
245
|
+
}
|
246
|
+
|
247
|
+
@portals = Rfm::Util::CaseInsensitiveHash.new
|
248
|
+
row_element.each_element('relatedset') { |relatedset|
|
249
|
+
table = relatedset.attributes['table']
|
250
|
+
records = []
|
251
|
+
relatedset.each_element('record') { |record|
|
252
|
+
records << Record.new(record, @resultset, @resultset.portals[table], @layout, table)
|
253
|
+
}
|
254
|
+
@portals[table] = records
|
255
|
+
}
|
256
|
+
|
257
|
+
@loaded = true
|
258
|
+
end
|
259
|
+
|
260
|
+
attr_reader :record_id, :mod_id, :portals
|
261
|
+
|
262
|
+
# Saves local changes to the Record object back to Filemaker. For example:
|
263
|
+
#
|
264
|
+
# myLayout.find({"First Name" => "Bill"}).each(|record|
|
265
|
+
# record["First Name"] = "Steve"
|
266
|
+
# record.save
|
267
|
+
# )
|
268
|
+
#
|
269
|
+
# This code finds every record with _Bill_ in the First Name field, then changes the first name to
|
270
|
+
# Steve.
|
271
|
+
#
|
272
|
+
# Note: This method is smart enough to not bother saving if nothing has changed. So there's no need
|
273
|
+
# to optimize on your end. Just save, and if you've changed the record it will be saved. If not, no
|
274
|
+
# server hit is incurred.
|
275
|
+
def save
|
276
|
+
self.merge(@layout.edit(self.record_id, @mods)[0]) if @mods.size > 0
|
277
|
+
@mods.clear
|
278
|
+
end
|
279
|
+
|
280
|
+
# Like Record::save, except it fails (and raises an error) if the underlying record in FileMaker was
|
281
|
+
# modified after the record was fetched but before it was saved. In other words, prevents you from
|
282
|
+
# accidentally overwriting changes someone else made to the record.
|
283
|
+
def save_if_not_modified
|
284
|
+
self.merge(@layout.edit(@record_id, @mods, {:modification_id => @mod_id})[0]) if @mods.size > 0
|
285
|
+
@mods.clear
|
286
|
+
end
|
287
|
+
|
288
|
+
# Gets the value of a field from the record. For example:
|
289
|
+
#
|
290
|
+
# first = myRecord["First Name"]
|
291
|
+
# last = myRecord["Last Name"]
|
292
|
+
#
|
293
|
+
# This sample puts the first and last name from the record into Ruby variables.
|
294
|
+
#
|
295
|
+
# You can also update a field:
|
296
|
+
#
|
297
|
+
# myRecord["First Name"] = "Sophia"
|
298
|
+
#
|
299
|
+
# When you do, the change is noted, but *the data is not updated in FileMaker*. You must call
|
300
|
+
# Record::save or Record::save_if_not_modified to actually save the data.
|
301
|
+
def []=(pname, value)
|
302
|
+
return super if !@loaded # this just keeps us from getting mods during initialization
|
303
|
+
name = pname
|
304
|
+
if self[name] != nil
|
305
|
+
@mods[name] = val
|
306
|
+
else
|
307
|
+
raise Rfm::Error::ParameterError.new("You attempted to modify a field called '#{name}' on the Rfm::Record object, but that field does not exist.")
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def method_missing (symbol, *attrs)
|
312
|
+
# check for simple getter
|
313
|
+
val = self[symbol.to_s]
|
314
|
+
return val if val != nil
|
315
|
+
|
316
|
+
# check for setter
|
317
|
+
symbol_name = symbol.to_s
|
318
|
+
if symbol_name[-1..-1] == '=' && self.has_key?(symbol_name[0..-2])
|
319
|
+
return @mods[symbol_name[0..-2]] = attrs[0]
|
320
|
+
end
|
321
|
+
super
|
322
|
+
end
|
323
|
+
|
324
|
+
def respond_to?(symbol, include_private = false)
|
325
|
+
return true if self[symbol.to_s] != nil
|
326
|
+
super
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# The Field object represents a single FileMaker field. It *does not hold the data* in the field. Instead,
|
331
|
+
# it serves as a source of metadata about the field. For example, if you're script is trying to be highly
|
332
|
+
# dynamic about its field access, it may need to determine the data type of a field at run time. Here's
|
333
|
+
# how:
|
334
|
+
#
|
335
|
+
# field_name = "Some Field Name"
|
336
|
+
# case myRecord.fields[field_name].result
|
337
|
+
# when "text"
|
338
|
+
# # it is a text field, so handle appropriately
|
339
|
+
# when "number"
|
340
|
+
# # it is a number field, so handle appropriately
|
341
|
+
# end
|
342
|
+
#
|
343
|
+
# =Attributes
|
344
|
+
#
|
345
|
+
# The Field object has the following attributes useful attributes:
|
346
|
+
#
|
347
|
+
# * *name* is the name of the field
|
348
|
+
#
|
349
|
+
# * *result* is the data type of the field; possible values include:
|
350
|
+
# * text
|
351
|
+
# * number
|
352
|
+
# * date
|
353
|
+
# * time
|
354
|
+
# * timestamp
|
355
|
+
# * container
|
356
|
+
#
|
357
|
+
# * *type* any of these:
|
358
|
+
# * normal (a normal data field)
|
359
|
+
# * calculation
|
360
|
+
# * summary
|
361
|
+
#
|
362
|
+
# * *max_repeats* is the number of repetitions (1 for a normal field, more for a repeating field)
|
363
|
+
#
|
364
|
+
# * *global* is +true+ is this is a global field, *false* otherwise
|
365
|
+
#
|
366
|
+
# Note: Field types match FileMaker's own values, but the terminology differs. The +result+ attribute
|
367
|
+
# tells you the data type of the field, regardless of whether it is a calculation, summary, or normal
|
368
|
+
# field. So a calculation field whose result type is _timestamp_ would have these attributes:
|
369
|
+
#
|
370
|
+
# * result: timestamp
|
371
|
+
# * type: calculation
|
372
|
+
#
|
373
|
+
# * *control& is a FieldControl object representing the sytle and value list information associated
|
374
|
+
# with this field on the layout.
|
375
|
+
#
|
376
|
+
# Note: Since a field can sometimes appear on a layout more than once, +control+ may be an Array.
|
377
|
+
# If you don't know ahead of time, you'll need to deal with this. One easy way is:
|
378
|
+
#
|
379
|
+
# controls = [myField.control].flatten
|
380
|
+
# controls.each {|control|
|
381
|
+
# # do something with the control here
|
382
|
+
# }
|
383
|
+
#
|
384
|
+
# The code above makes sure the control is always an array. Typically, though, you'll know up front
|
385
|
+
# if the control is an array or not, and you can code accordingly.
|
386
|
+
|
387
|
+
class Field
|
388
|
+
|
389
|
+
# Initializes a field object. You'll never need to do this. Instead, get your Field objects from
|
390
|
+
# ResultSet::fields
|
391
|
+
def initialize(result_set, field)
|
392
|
+
@result_set = result_set
|
393
|
+
@name = field.attributes['name']
|
394
|
+
@result = field.attributes['result']
|
395
|
+
@type = field.attributes['type']
|
396
|
+
@max_repeats = field.attributes['max-repeats']
|
397
|
+
@global = field.attributes['global']
|
398
|
+
|
399
|
+
@laded = false
|
400
|
+
end
|
401
|
+
|
402
|
+
attr_reader :name, :result, :type, :max_repeats, :global
|
403
|
+
|
404
|
+
def control
|
405
|
+
@result_set.layout.field_controls[@name]
|
406
|
+
end
|
407
|
+
|
408
|
+
# Coerces the text value from an +fmresultset+ document into proper Ruby types based on the
|
409
|
+
# type of the field. You'll never need to do this: Rfm does it automatically for you when you
|
410
|
+
# access field data through the Record object.
|
411
|
+
def coerce(value)
|
412
|
+
return nil if (value == nil || value == '') && @result != "text"
|
413
|
+
case @result
|
414
|
+
when "text"
|
415
|
+
return value
|
416
|
+
when "number"
|
417
|
+
return BigDecimal.new(value)
|
418
|
+
when "date"
|
419
|
+
return Date.strptime(value, @result_set.date_format)
|
420
|
+
when "time"
|
421
|
+
return DateTime.strptime("1/1/-4712 " + value, "%m/%d/%Y #{@result_set.time_format}")
|
422
|
+
when "timestamp"
|
423
|
+
return DateTime.strptime(value, @result_set.timestamp_format)
|
424
|
+
when "container"
|
425
|
+
return URI.parse("#{@result_set.server.scheme}://#{@result_set.server.host_name}:#{@result_set.server.port}#{value}")
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
end
|
data/lib/rfm_utility.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require '../lib/rfm'
|
3
|
+
|
4
|
+
# Test cases for testing the FileMakerError classes
|
5
|
+
#
|
6
|
+
# Author:: Mufaddal Khumri
|
7
|
+
# Copyright:: Copyright (c) 2007 Six Fried Rice, LLC and Mufaddal Khumri
|
8
|
+
# License:: See MIT-LICENSE for details
|
9
|
+
class TC_TestErrors < Test::Unit::TestCase
|
10
|
+
|
11
|
+
def test_default_message_system_errors
|
12
|
+
begin
|
13
|
+
raise Rfm::Error::FileMakerError.getError(0)
|
14
|
+
rescue Rfm::Error::SystemError => ex
|
15
|
+
assert_equal(ex.message, 'SystemError occurred.')
|
16
|
+
assert_equal(ex.code, 0)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_scriptmissing_errors
|
21
|
+
begin
|
22
|
+
raise Rfm::Error::FileMakerError.getError(104, 'ScriptMissingError occurred.')
|
23
|
+
rescue Rfm::Error::MissingError => ex
|
24
|
+
assert_equal(ex.code, 104)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_rangevalidation_errors
|
29
|
+
begin
|
30
|
+
raise Rfm::Error::FileMakerError.getError(503, 'RangeValidationError occurred.')
|
31
|
+
rescue Rfm::Error::ValidationError => ex
|
32
|
+
assert_equal(ex.code, 503)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_one_unknown_errors
|
37
|
+
begin
|
38
|
+
raise Rfm::Error::FileMakerError.getError(-1, 'UnknownError occurred.')
|
39
|
+
rescue Rfm::Error::UnknownError => ex
|
40
|
+
assert_equal(ex.code, -1)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_two_unknown_errors
|
45
|
+
begin
|
46
|
+
raise Rfm::Error::FileMakerError.getError(99999, 'UnknownError occurred.')
|
47
|
+
rescue Rfm::Error::UnknownError => ex
|
48
|
+
assert_equal(ex.code, 99999)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/test/rfm_tester.rb
ADDED
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lardawge-rfm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Geoff Coffey
|
8
|
+
- Mufaddal Khumri
|
9
|
+
- Atsushi Matsuo
|
10
|
+
- Larry Sprock
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
|
15
|
+
date: 2009-05-29 00:00:00 -07:00
|
16
|
+
default_executable:
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: hpricot
|
20
|
+
type: :runtime
|
21
|
+
version_requirement:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.1
|
27
|
+
version:
|
28
|
+
description: Rfm brings your FileMaker data to Ruby with elegance and speed. Now your Ruby scripts and Rails applications can talk directly to your FileMaker server with a syntax that just feels right.
|
29
|
+
email: http://groups.google.com/group/rfmcommunity
|
30
|
+
executables: []
|
31
|
+
|
32
|
+
extensions: []
|
33
|
+
|
34
|
+
extra_rdoc_files:
|
35
|
+
- README.rdoc
|
36
|
+
files:
|
37
|
+
- lib/rfm.rb
|
38
|
+
- lib/rfm_command.rb
|
39
|
+
- lib/rfm_error.rb
|
40
|
+
- lib/rfm_factory.rb
|
41
|
+
- lib/rfm_result.rb
|
42
|
+
- lib/rfm_utility.rb
|
43
|
+
- README.rdoc
|
44
|
+
has_rdoc: false
|
45
|
+
homepage: http://sixfriedrice.com/wp/products/rfm/
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.2.0
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: FileMaker to Ruby adapter
|
70
|
+
test_files:
|
71
|
+
- test/rfm_test_errors.rb
|
72
|
+
- test/rfm_tester.rb
|