ginjo-rfm 1.4.4 → 2.0.pre31

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,107 @@
1
+ # Changelog
2
+
3
+ ## Ginjo-Rfm 2.0.pre
4
+
5
+ * Alternative XML parsers using ActiveSupport::XmlMini interface
6
+
7
+ * ActiveModel compatibility allows Rails-like models
8
+
9
+ * Complex queries with Layout#query method
10
+
11
+ * Configuration API manages settings of multiple server/db/layout/etc setups
12
+
13
+ * Full Filemaker metadata support
14
+
15
+ ## Ginjo-Rfm 1.4.4
16
+
17
+ * Fixed bug when creating empty value list.
18
+
19
+ * Additional fixes for Rfm::VERSION.
20
+
21
+ * Fixed Record getter/setter issue.
22
+
23
+ * Other minor fixes and cleanup.
24
+
25
+ * Added tests to rspec.
26
+
27
+ * Documentation cleanup.
28
+
29
+ ## Ginjo-Rfm 1.4.3
30
+
31
+ * Fixed version management issue. Rfm::VERSION now works.
32
+
33
+ ## Ginjo-Rfm 1.4.2
34
+
35
+ * Re-implemented:
36
+
37
+ Layout#field_controls
38
+
39
+ Layout#value_lists
40
+
41
+ * Enhanced:
42
+
43
+ ValueListItem handles both display & data items now.
44
+
45
+ Timeout feature from timting (github/timting/rfm).
46
+
47
+ Added specs for Record#save.
48
+
49
+ * Fixed:
50
+
51
+ [Bug] Getting & setting fields with symbol-based keys was producing error.
52
+
53
+ [Bug] Setting fields would not update main record hash.
54
+
55
+ [Bug] Record#save wasn't merging back into self.
56
+
57
+ * Partial Fix:
58
+
59
+ server.db.all
60
+ db.layout.all
61
+ db.script.all
62
+
63
+ Note: the "#all" method returns object names (as keys) only. The receiver of the method maintains the full object collection.
64
+
65
+ Example:
66
+
67
+ server.db.all #=> ['dbname1', 'dbname2', ...]
68
+ server.db #=> a DbFactory object (descendant of Hash), containing 0 or more Database objects
69
+
70
+ ## Lardawge-Rfm 1.4.2 (unreleased)
71
+
72
+ * Made nil default on fields with no value.
73
+
74
+ Example:
75
+
76
+ Old: record.john #=> ""
77
+ New: record.john #=> nil
78
+
79
+ ## Lardawge-Rfm 1.4.1.2
80
+
81
+ * [Bug] Pointing out why testing is soooooo important when refactoring... Found a bug in getter/setter method in Rfm::Record (yes, added spec for it).
82
+
83
+ ## Lardawge-Rfm 1.4.1.1
84
+
85
+ * [Bug] Inadvertently left out an attr_reader for server from resultset effecting container urls.
86
+
87
+ ## Lardawge-Rfm 1.4.1*
88
+
89
+ * Changed Server#do_action to Server#connect.
90
+
91
+ * XML Parsing is now done via xpath which significantly speeds up parsing.
92
+
93
+ * Changes to accessor method names for Resultset#portals Resultset#fields to Resultset#portal_meta and Resultset#field_meta to better describe what you get back.
94
+
95
+ * Added an option to load portal records which defaults to false. This significantly speeds up load time when portals are present on the layout.
96
+
97
+ Example:
98
+
99
+ result = fm_server('layout').find({:username => "==#{username}"}, {:include_portals => true})
100
+ # => This will fetch all records with portal records attached.
101
+
102
+ result.first.portals
103
+ # => would return an empty hash by default.
104
+
105
+ * Internal file restructuring. Some classes have changed but it should be nothing a developer would use API wise. Please let me know if it is.
106
+
107
+ * Removed Layout#value_lists && Layout#field_controls. Will put back in if the demand is high. Needs a major refactor and different placement if it goes back in. Was broken so it didn't seem to be used by many devs.
data/README.md CHANGED
@@ -1,180 +1,413 @@
1
1
  # ginjo-rfm
2
2
 
3
- Rfm is a Ruby/Filemaker adapter - a ruby gem that allows scripts and applications to exchange commands and data with Filemaker Pro using Filemaker's XML interface. Ginjo-rfm picks up from the lardawge-rfm gem and continues to refine code and fix bugs. It also adds minor enhancements like server timeout and value-list alternate display. To read more about Rfm, see the info at [Sixfriedrice](http://sixfriedrice.com/wp/products/rfm/), or check out the [RDoc](http://rubydoc.info/github/ginjo/rfm/master/frames).
3
+ Rfm is a Ruby/Filemaker adapter - a ruby gem that allows scripts and applications to exchange commands and data with Filemaker Pro using Filemaker's XML interface. Ginjo-rfm picks up from the lardawge-rfm gem and continues to refine code and fix bugs. Version 2.0 adds some major enhancements, while remaining compatible with ginjo-rfm 1.4.x and lardawge-rfm 1.4.x.
4
+
5
+
6
+ ## Documentation & Links
7
+
8
+ * Ginjo-rfm rubygem <https://rubygems.org/gems/ginjo-rfm>
9
+ * Original homepage <http://sixfriedrice.com/wp/products/rfm/>
10
+ * Rdoc location <http://rubydoc.info/github/ginjo/rfm/frames>
11
+ * Discussion <http://groups.google.com/group/rfmcommunity>
12
+ * Ginjo at github <https://github.com/ginjo/rfm>
13
+ * Lardawge at github <https://github.com/lardawge/rfm>
14
+
15
+
16
+ ## New in version 2.0
17
+
18
+ ### Data modeling with ActiveModel and graceful degradation without ActiveModel.
19
+
20
+ If you can load ActiveModel in your project, you can have model callbacks, validations, and other ActiveModel features.
21
+ If you can't load ActiveModel (because you're using something incompatible, like Rails 2),
22
+ you can still use Rfm models... minus callbacks & validations. Rfm models give you basic
23
+ data modeling with easy configuration and CRUD features.
24
+
25
+ class User < Rfm::Base
26
+ config :layout => 'user_layout'
27
+ end
28
+
29
+ @user = User.find 12345
30
+ @user.update_attributes(:name => 'bill', :login => 'admin')
31
+ @user.save!
32
+
33
+ With ActiveModel loaded, you get callbacks, validations, and many other ActiveModel features.
34
+
35
+ class User < Rfm::Base
36
+ config :layout=>'user_layout'
37
+ before_save :encrypt_password
38
+ validate :valid_email_address
39
+ end
40
+
41
+ @user = User.new :username => 'bill', :password => 'pass'
42
+ @user.email = 'my@email.com'
43
+ @user.save!
44
+
45
+ If you prefer, you can create models on-the-fly from any layout.
46
+
47
+ my_layout.modelize
48
+
49
+ # => MyLayoutName (subclassed from Rfm::Base, represented by your layout's name)
50
+
51
+ Or create models for an entire database, all at once.
52
+
53
+ Rfm.modelize :my_db_name_or_config
54
+
55
+ # => [MyLayout, AnotherLayout, ThirdLayout, AndSoOn, ...]
56
+
57
+
58
+ ### Choice of XML parsers
59
+
60
+ Ginjo-rfm 2.0 uses ActiveSupport's XmlMini parsing interface, which has built-in support for
61
+ LibXML, Nokogiri, and REXML. Additionally, ginjo-rfm includes a module for Hpricot parsing.
62
+ You can specifiy which parser to use or load them all and let Rfm decide.
63
+
64
+ Rfm.config :parser => :libxml
65
+
66
+ If you're not able to install one of the faster parsers, ginjo-rfm will fall back to
67
+ ruby's built-in REXML. Want to roll your own XML adapter? Just pass it to Rfm as a module.
68
+
69
+ Rfm.config :parser => MyHomeGrownAdapter
70
+
71
+ Choose your preferred parser globaly, as in the above example, or set a different parser for each model.
72
+
73
+ class Order < Rfm::Base
74
+ config :parser => :hpricot
75
+ end
76
+
77
+ Not only do you have 4 XML backend parsers to choose from, but you also have the option of choosing from different parsing shemes - the DOM parsing scheme, or the streaming (SAX or SAX-like) scheme. This gives you six different available parsing possilities.
78
+
79
+ * LibXML DOM
80
+ * LibXML SAX
81
+ * Nokogiri DOM
82
+ * Nokogiri SAX
83
+ * Hpricot DOM
84
+ * REXML DOM
4
85
 
5
- Rfm was primarily designed by Six Fried Rice co-founder Geoff Coffey.
86
+ ### Configuration API
6
87
 
7
- Other lead contributors:
88
+ The ginjo-rfm configuration module is a heirarchical system that allows you to configure settings at a global level
89
+ and then recall just the settings you need, where you need them. Configuration settings can be simple
90
+ values, or they can be named groups of values.
8
91
 
9
- * Mufaddal Khumri helped architect Rfm in the most ruby-like way possible. He also contributed the outstanding error handling code and a comprehensive hierarchy of error classes.
10
- * Atsushi Matsuo was an early Rfm tester, and provided outstanding feedback, critical code fixes, and a lot of web exposure.
11
- * Jesse Antunes helped ensure that Rfm is stable and functional.
12
- * Larry Sprock added ssl support, switched the xml parser to a much faster Nokogiri, added the rspec testing framework, and refactored the code to pave way for future development.
92
+ For simple applications, put all of your configuration in a top-level hash, RFM_CONFIG,
93
+ and let Rfm do the rest. For more complicated setups, use configuration subgroups,
94
+ and/or set configuration on-the-fly when you create Server, Database, Layout, or Base objects.
13
95
 
14
- Documentation & Links
96
+ Use RFM_CONFIG
15
97
 
16
- * Ginjo-rfm homepage: <https://rubygems.org/gems/ginjo-rfm>
17
- * Original homepage: <http://sixfriedrice.com/wp/products/rfm/>
18
- * Rdoc location: <http://rubydoc.info/github/ginjo/rfm/frames>
19
- * Discussion: <http://groups.google.com/group/rfmcommunity>
20
- * Ginjo-rfm <https://github.com/ginjo/rfm>
21
- * Lardawge-rfm <https://github.com/lardawge/rfm>
98
+ RFM_CONFIG = {
99
+ :host => 'main_host',
100
+ :database => 'main_database',
101
+ :account_name => 'myname',
102
+ :password => 'somepass',
103
+ :second_server => {
104
+ :host => 'second_host',
105
+ :database => 'second_database'
106
+ }
22
107
 
23
- Partial list of features added since rfm 1.0.0.
108
+ Or set global configuration with the 'config' method
24
109
 
25
- * SSL support
26
- * Nokogiri xml parser
27
- * Display-vs-data value lists
28
- * Connection timeout
29
- * Metadata support re-introduced
110
+ Rfm.config :host => 'main_host',
111
+ :database => 'main_database',
112
+ :account_name => 'myname',
113
+ :password => 'somepass',
114
+ :second_server => {
115
+ :host => 'second_host',
116
+ :database => 'second_database'
117
+ }
118
+
119
+ Set configuration of RFM::Base
30
120
 
31
- Partial roadmap to the future
121
+ Rfm::Base.config :ssl => true
32
122
 
33
- * ActiveModel compatibility
34
- * Alternative XML parsers
35
- * Complex Filemaker queries
36
- * More tests
123
+ Set a model's configuration
124
+
125
+ class MyClass < Rfm::Base
126
+ config :second_server, :layout => 'mylayout'
127
+ end
128
+
129
+ View model-specific configuration
130
+
131
+ MyClass.config
132
+
133
+ # => {:host => 'second_host', :database => 'second_database'}
37
134
 
135
+ View the merged configurations of all relevent levels in the configuration chain.
38
136
 
39
- ## Installation
137
+ MyClass.get_config
138
+
139
+ # => {:host => 'second_host', :database => 'second_database', :account_name => 'myname', :password => 'somepass', :ssl => true}
40
140
 
41
- Rfm depends on Nokogiri gem, which installs executables requiring C compilation. Make sure you have a C compiler installed, ruby development headers, and Nokogiri's pre-requisite libxml2 & libxslt (or Xcode on OS X). For help installing [Nokogiri](http://nokogiri.org/), see their helpful [tutorial](http://nokogiri.org/tutorials/installing_nokogiri.html). Future versions of ginjo-rfm will offer alternative XML parsing options, hopefully making it easier to get up and running.
42
141
 
43
- Terminal:
142
+ Calling the get_config method will show you what compilation of config settings are seen at any given point in Rfm and/or in your application. The current heirarchy of configurable objects in Rfm, starting at the top:
44
143
 
45
- #bash
46
- gem install ginjo-rfm
144
+ * RFM_CONFIG # a user-defined hash
145
+ * Rfm::Config # top-level config module
146
+ * Rfm::Factory # where server, database, and layout objects are managed
147
+ * Rfm::Base # master modeling class
148
+ * MyModel # custom modeling class
47
149
 
48
- Once the gem is installed, you can use rfm in your ruby scripts by requiring it:
49
150
 
50
- require 'rubygems'
51
- require 'rfm'
52
151
 
53
- ### In Rails >= 3.0, or any project using Bundler
152
+
153
+ ### Complex Queries
154
+
155
+ Create queries with mixed boolean logic, mimicing Filemaker's multiple-request find.
156
+
157
+ layout.query :fieldOne => ['=val1','>=val2','<val3'], :fieldTwo =>'someValue'
158
+
159
+ This will create 3 "find requests" (in a single call to FM Server), one for each value in the fieldOne array, AND'd with the fieldTwo value.
160
+
161
+
162
+ ### Full Metadata Support
163
+
164
+ * Server databases
165
+ * Database layouts
166
+ * Database scripts
167
+ * Layout fields
168
+ * Layout portals
169
+ * Resultset meta
170
+ * Field meta
171
+ * Portal meta
172
+
173
+ From ginjo-rfm 1.4.x, the following enhancements are also included.
174
+
175
+ * Connection timeout settings
176
+
177
+ * Value-list alternate display
178
+
179
+ There are also many enhancements to make it easier than ever to get the objects or data you want. Some examples:
180
+
181
+ Get a database object using default config
182
+
183
+ Rfm.db 'my_db'
184
+
185
+ Get a layout object using config grouping :my_group
186
+
187
+ Rfm.layout :my_group
188
+
189
+ Get the total count of all records in the table
190
+
191
+ MyModel.total_count
192
+
193
+ Get the portal names (table-occurence names) on the current layout
194
+
195
+ MyModel.portal_names
196
+
197
+ Get the names of fields on the current layout
198
+
199
+ my_record.field_names
200
+
201
+ ### Compatibility
202
+
203
+ Ginjo-rfm 2.0 is compatible with previous versions of Rfm - Ginjo, Lardawge, and SFR. However, much has been changed in the low-level workings of the code, in orer to pave the way for data modeling and flexible XML adapters. If you have scripts that reach deep into the guts of Rfm 1.0 thru 1.4.x, you may find that some things are slightly different in 2.0. Additionally, some long-standing bugs have been fixed that may have been so de rigeur, that the "correct behavior" in Rfm 2.0 may break scripts that relied on the previously buggy functions. These low level changes, and the addition of major new functionality, led the decision to release this version of Rfm as 2.0, instead of 1.5.
204
+
205
+
206
+ ## Installation
207
+
208
+ Ginjo-rfm requires ActiveSupport for several features, including XML parsing. Rfm has been tested and works with ActiveSupport 2.3.5 thru 3.1.3. ActiveModel requires ActiveSupport 3+ and is not compatible with ActiveSupport 2.3.x. So while you CAN use ginjo-rfm with Rails 2.3, you will not have ActiveModel features like callbacks and validations. Basic model functionality and Filemaker interaction will continue to work, unaffected by the presence or absence of ActiveModel.
209
+
210
+ To get the best performance, it is recommended that you use the LibXML or Nokogiri parser. Ginjo-rfm does not require these gems by dependency, so you will have to make sure they are installed on your machine and/or specified in your Gemfile, if you wish to use them. Similarly, ginjo-rfm does not require ActiveModel by dependency, so also make sure that is installed and/or specified in your Gemfile, if you wish to use ActiveModel features.
211
+
212
+ ### Using Bundler and/or Rails >= 3.0
54
213
 
55
214
  In the Gemfile:
56
215
 
57
- gem 'ginjo-rfm'
216
+ gem 'ginjo-rfm'
217
+ gem 'libxml-ruby' # optional
218
+ gem 'nokogiri' # optional
219
+ gem 'hpricot' # optional
220
+ gem 'activemodel' # optional
221
+
222
+ In your shell:
223
+
224
+ bundle install
225
+
226
+ In your project, you may or may not have to require 'rfm', depending on Bundler's configuration:
227
+
228
+ require 'rfm'
229
+
230
+ ### Without Bundler
231
+
232
+ If you are not using Bundler, Rfm will pick up the XML parsers and ActiveModel as long as they are available in your current rubygems installation.
233
+
234
+ In your shell:
235
+
236
+ gem install ginjo-rfm
237
+ gem install nokogiri # optional
238
+ gem install libxml-ruby # optional
239
+ gem install hpricot # optional
240
+ gem install activemodel # optional
241
+
242
+ Once the gem is installed, you can use rfm in your ruby scripts by requiring it:
243
+
244
+ require 'rubygems'
245
+ require 'rfm'
246
+
247
+
58
248
 
59
249
  ### Edge - in an upcoming version of ginjo-rfm
60
250
 
61
- ActiveModel support.
62
-
63
- class Account < Rfm::Base
64
- config :layout=>'account_xml'
65
- before_create :encrypt_password
66
- validates :email, :presence => true
67
- validates :username, :presence => true
68
- attr_accessor :password
69
- end
70
-
71
- @account = Account.new(:username=>'bill', :password=>'pass')
72
- @account.email = 'my@email.com'
73
- @account.save!
74
-
75
- Multiple backend xml parsers.
76
-
77
- Rfm::Server.new(:backend => :nokogiri)
78
- # Backend options => :libxml, :libxmlsax, :nokogiri, :nokogirisax, :hpricot, :rexml
79
-
80
- Try out these unreleased features in the edge branch.
81
-
82
- #gemfile
83
- gem 'ginjo-rfm', :git=>'git://github.com/ginjo/rfm.git', :branch=>'edge'
84
-
251
+ Try out unreleased features of ginjo-rfm in the edge branch.
252
+
253
+ #gemfile
254
+ gem 'ginjo-rfm', :git=>'git://github.com/ginjo/rfm.git', :branch=>'edge'
255
+
256
+
257
+
258
+ ## Basic usage
259
+
260
+ Put your configuration settings in a hash represented by RFM_CONFIG. This will make it easier to get and use objects in Rfm.
261
+
262
+ RFM_CONFIG = {
263
+ :host => 'main_host',
264
+ :database => 'main_database',
265
+ :account_name => 'myname',
266
+ :password => 'somepass',
267
+ :ssl => false,
268
+ :second_server => {
269
+ :host => 'second_host',
270
+ :database => 'second_database'
271
+ }
272
+
273
+ Then you have two easy ways to access your layouts - and your data.
274
+
275
+
276
+ ### With models
277
+
278
+ Rfm models provide easy access to the record-finder functions of Rfm layouts, and they give us a way to easily persist objects to the database.
279
+
280
+ class User < Rfm::Base
281
+ config :layout => 'my_layout_name'
282
+ end
283
+
284
+ @user = User.new(:login => 'bill', :password => 'xxxxxxxx', :email => 'my@email.com')
285
+ @user.save!
286
+
287
+ @user.login
288
+ # => 'bill'
289
+
290
+ @user.field_names
291
+ # => ['login', 'encryptedPassword', 'email', 'groups', 'lastLogin' ]
292
+
293
+ User.field_names
294
+ # => ['login', 'encryptedPassword', 'email', 'groups', 'lastLogin' ]
295
+
296
+ ### Manually
297
+
298
+ Create a layout object using default configuration settings.
299
+
300
+ my_layout = Rfm.layout 'layout_name'
301
+
302
+ Create a layout object using a subgroup of configuration settings.
303
+
304
+ my_layout = Rfm.layout :subgroup_name
305
+
306
+ Create a layout object passing in a layout name, multiple config subgroups to merge, and specific settings.
307
+
308
+ my_layout = Rfm.layout 'layout_name', :second_server, :log_actions => true
309
+
310
+
311
+ Once you have an Rfm model or layout, you can use any of the standard Rfm commands to create, search, edit, and delete records. To learn more about these commands, see below for Databases, Layouts, Resultsets, and Records. Or checkout the documentation for Rfm::Layout, Rfm::Record, and Rfm::Base.
312
+
313
+
314
+ # Working with "classic" Rfm features
315
+
316
+ All of Rfm's original features and functions are available as they were before, though some low-level functionality has changed slightly.
317
+
318
+
85
319
  ## Connecting
86
320
 
87
321
  IMPORTANT:SSL and Certificate verification are on by default. Please see Server#new in rdocs for explanation and setup.
88
322
  You connect with the Rfm::Server object. This little buddy will be your window into FileMaker data.
89
323
 
90
- require 'rfm'
324
+ require 'rfm'
91
325
 
92
- my_server = Rfm::Server.new(
93
- :host => 'myservername',
94
- :account_name => 'user',
95
- :password => 'pw',
96
- :ssl => false
97
- )
326
+ my_server = Rfm::Server.new(
327
+ :host => 'myservername',
328
+ :account_name => 'user',
329
+ :password => 'pw',
330
+ :ssl => false
331
+ )
98
332
 
99
333
  if your web publishing engine runs on a port other than 80, you can provide the port number as well:
100
334
 
101
- my_server = Rfm::Server.new(
102
- :host => 'myservername',
103
- :account_name => 'user',
104
- :password => 'pw',
105
- :port => 8080,
106
- :ssl => false,
107
- :root_cert => false
108
- )
335
+ my_server = Rfm::Server.new(
336
+ :host => 'myservername',
337
+ :account_name => 'user',
338
+ :password => 'pw',
339
+ :port => 8080,
340
+ :ssl => false,
341
+ :root_cert => false
342
+ )
109
343
 
110
344
  ## Databases and Layouts
111
345
 
112
346
  All access to data in FileMaker's XML interface is done through layouts, and layouts live in databases. The Rfm::Server object has a collection of databases called 'db'. So to get ahold of a database called "My Database", you can do this:
113
347
 
114
- my_db = my_server.db["My Database"]
348
+ my_db = my_server.db["My Database"]
115
349
 
116
350
  As a convenience, you can do this too:
117
351
 
118
- my_db = my_server["My Database"]
352
+ my_db = my_server["My Database"]
119
353
 
120
354
  Finally, if you want to introspect the server and find out what databases are available, you can do this:
121
355
 
122
- all_dbs = my_server.db.all
356
+ all_dbs = my_server.db.all
123
357
 
124
358
  In any case, you get back Rfm::Database objects. A database object in turn has a property called "layout":
125
359
 
126
- my_layout = my_db.layout["My Layout"]
360
+ my_layout = my_db.layout["My Layout"]
127
361
 
128
362
  Again, for convenience:
129
363
 
130
- my_layout = my_db["My Layout"]
364
+ my_layout = my_db["My Layout"]
131
365
 
132
366
  And to get them all:
133
367
 
134
- all_layouts = my_db.layout.all
368
+ all_layouts = my_db.layout.all
135
369
 
136
370
  Bringing it all together, you can do this to go straight from a server to a specific layout:
137
371
 
138
- my_layout = my_server["My Database"]["My Layout"]
372
+ my_layout = my_server["My Database"]["My Layout"]
139
373
 
140
374
  ## Working with Layouts
141
375
 
142
376
  Once you have a layout object, you can start doing some real work. To get every record from the layout:
143
377
 
144
- my_layout.all # be careful with this
378
+ my_layout.all # be careful with this
145
379
 
146
380
  To get a random record:
147
381
 
148
- ruby
149
- my_layout.any
382
+ my_layout.any
150
383
 
151
384
  To find every record with "Arizona" in the "State" field:
152
385
 
153
- my_layout.find({"State" => "Arizona"})
386
+ my_layout.find({"State" => "Arizona"})
154
387
 
155
388
  To add a new record with my personal info:
156
389
 
157
- my_layout.create({
158
- :first_name => "Geoff",
159
- :last_name => "Coffey",
160
- :email => "gwcoffey@gmail.com"}
161
- )
390
+ my_layout.create({
391
+ :first_name => "Geoff",
392
+ :last_name => "Coffey",
393
+ :email => "gwcoffey@gmail.com"}
394
+ )
162
395
 
163
396
  Notice that in this case I used symbols instead of strings for the hash keys. The API will accept either form, so if your field names don't have whitespace or punctuation, you might prefer the symbol notation.
164
397
 
165
- To edit the record whos recid (filemaker internal record id) is 200:
398
+ To edit the record whose recid (filemaker internal record id) is 200:
166
399
 
167
- my_layout.edit(200, {:first_name => 'Mamie'})
400
+ my_layout.edit(200, {:first_name => 'Mamie'})
168
401
 
169
402
  Note: See the "Record Objects" section below for more on editing records.
170
403
 
171
404
  To delete the record whose recid is 200:
172
405
 
173
- my_layout.delete(200)
406
+ my_layout.delete(200)
174
407
 
175
408
  All of these methods return an Rfm::Result::ResultSet object (see below), and every one of them takes an optional parameter (the very last one) with additional options. For example, to find just a page full of records, you can do this:
176
409
 
177
- my_layout.find({:state => "AZ"}, {:max_records => 10, :skip_records => 100})
410
+ my_layout.find({:state => "AZ"}, {:max_records => 10, :skip_records => 100})
178
411
 
179
412
  For a complete list of the available options, see the "expand_options" method in the Rfm::Server object in the file named rfm_command.rb.
180
413
 
@@ -185,84 +418,84 @@ Finally, if filemaker returns an error when executing any of these methods, an e
185
418
 
186
419
  Any method on the Layout object that returns data will return a ResultSet object. Rfm::Result::ResultSet is a subclass of Array, so first and foremost, you can use it like any other array:
187
420
 
188
- my_result = my_layout.any
189
- my_result.size # returns '1'
190
- my_result[0] # returns the first record (an Rfm::Result::Record object)
421
+ my_result = my_layout.any
422
+ my_result.size # returns '1'
423
+ my_result[0] # returns the first record (an Rfm::Result::Record object)
191
424
 
192
425
  The ResultSet object also tells you information about the fields and portals in the result. ResultSet#fields and ResultSet#portals are both standard ruby hashes, with strings for keys. The fields hash has Rfm::Result::Field objects for values. The portals hash has another hash for its values. This nested hash is the fields on the portal. This would print out all the field names:
193
426
 
194
- my_result.fields.each { |name, field| puts name }
427
+ my_result.fields.each { |name, field| puts name }
195
428
 
196
429
  This would print out the tables each portal on the layout is associated with. Below each table name, and indented, it will print the names of all the fields on each portal.
197
430
 
198
- my_result.portals.each { |table, fields|
199
- puts "table: #{table}"
200
- fields.each { |name, field| puts "\t#{name}"}
201
- }
431
+ my_result.portals.each { |table, fields|
432
+ puts "table: #{table}"
433
+ fields.each { |name, field| puts "\t#{name}"}
434
+ }
202
435
 
203
436
  But most importantly, the ResultSet contains record objects. Rfm::Result::Record is a subclass of Hash, so it can be used in many standard ways. This code would print the value in the 'first_name' field in the first record of the ResultSet:
204
437
 
205
- my_record = my_result[0]
206
- puts my_record["first_name"]
438
+ my_record = my_result[0]
439
+ puts my_record["first_name"]
207
440
 
208
441
  As a convenience, if your field names are valid ruby method names (ie, they don't have spaces or odd punctuation in them), you can do this instead:
209
442
 
210
- puts my_record.first_name
443
+ puts my_record.first_name
211
444
 
212
445
  Since ResultSets are arrays and Records are hashes, you can take advantage of Ruby's wonderful expressiveness. For example, to get a comma-separated list of the full names of all the people in California, you could do this:
213
446
 
214
- my_layout.find(:state => 'CA').collect {|rec| "#{rec.first_name} #{rec.last_name}"}.join(", ")
447
+ my_layout.find(:state => 'CA').collect {|rec| "#{rec.first_name} #{rec.last_name}"}.join(", ")
215
448
 
216
449
  Record objects can also be edited:
217
450
 
218
- my_record.first_name = 'Isabel'
451
+ my_record.first_name = 'Isabel'
219
452
 
220
453
  Once you have made a series of edits, you can save them back to the database like this:
221
454
 
222
- my_record.save
455
+ my_record.save
223
456
 
224
457
  The save operation causes the record to be reloaded from the database, so any changes that have been made outside your script will also be picked up after the save.
225
458
 
226
459
  If you want to detect concurrent modification, you can do this instead:
227
460
 
228
- my_record.save_if_not_modified
461
+ my_record.save_if_not_modified
229
462
 
230
463
  This version will refuse to update the database and raise an error if the record was modified after it was loaded but before it was saved.
231
464
 
232
465
  Record objects also have portals. While the portals in a ResultSet tell you about the tables and fields the portals show, the portals in a Record have the actual data. For example, if an Order record has Line Item records, you could do this:
233
466
 
234
- my_order = order_layout.any[0] # the [0] is important!
235
- my_lines = my_order.portals["Line Items"]
467
+ my_order = order_layout.any[0] # the [0] is important!
468
+ my_lines = my_order.portals["Line Items"]
236
469
 
237
470
  At the end of the previous block of code, my_lines is an array of Record objects. In this case, they are the records in the "Line Items" portal for the particular order record. You can then operate on them as you would any other record.
238
471
 
239
472
  NOTE: Fields on a portal have the table name and the "::" stripped off of their names if they belong to the table the portal is tied to. In other words, if our "Line Items" portal includes a quantity field and a price field, you would do this:
240
473
 
241
- my_lines[0]["Quantity"]
242
- my_lines[0]["Price"]
474
+ my_lines[0]["Quantity"]
475
+ my_lines[0]["Price"]
243
476
 
244
477
  You would NOT do this:
245
478
 
246
- my_lines[0]["Line Items::Quantity"]
247
- my_lines[0]["Line Items::Quantity"]
479
+ my_lines[0]["Line Items::Quantity"]
480
+ my_lines[0]["Line Items::Quantity"]
248
481
 
249
482
  My feeling is that the table name is redundant and cumbersome if it is the same as the portal's table. This is also up for debate.
250
483
 
251
484
  Again, you can string things together with Ruby. This will calculate the total dollar amount of the order:
252
485
 
253
- total = 0.0
254
- my_order.portals["Line Items"].each {|line| total += line.quantity * line.price}
486
+ total = 0.0
487
+ my_order.portals["Line Items"].each {|line| total += line.quantity * line.price}
255
488
 
256
489
  ## Data Types
257
490
 
258
491
  FileMaker's field types are coerced to Ruby types thusly:
259
492
 
260
- Text Field -> String object
261
- Number Field -> BigDecimal object # see below
262
- Date Field -> Date object
263
- Time Field -> DateTime object # see below
264
- TimeStamp Field -> DateTime object
265
- Container Field -> URI object
493
+ Text Field -> String object
494
+ Number Field -> BigDecimal object # see below
495
+ Date Field -> Date object
496
+ Time Field -> DateTime object # see below
497
+ TimeStamp Field -> DateTime object
498
+ Container Field -> URI object
266
499
 
267
500
  FileMaker's number field is insanely robust. The only data type in ruby that can handle the same magnitude and precision of a FileMaker number is Ruby's BigDecimal. (This is an extension class, so you have to require 'bigdecimal' to use it yourself). Unfortuantely, BigDecimal is not a "normal" ruby numeric class, so it might be really annoying that your tiny filemaker numbers have to go this route. This is a great topic for debate.
268
501
 
@@ -270,9 +503,9 @@ Also, Ruby doesn't have a Time type that stores just a normal time (with no date
270
503
 
271
504
  Finally, container fields will come back as URI objects. You can:
272
505
 
273
- - use Net::HTTP to download the contents of the container field using this URI
274
- - to_s the URI and use it as the src attribute of an HTML image tag
275
- - etc...
506
+ - use Net::HTTP to download the contents of the container field using this URI
507
+ - to_s the URI and use it as the src attribute of an HTML image tag
508
+ - etc...
276
509
 
277
510
  Specifically, the URI refers to the _contents_ of the container field. When accessed, the file, picture, or movie in the field will be downloaded.
278
511
 
@@ -288,13 +521,25 @@ When this is 'true' your script will dump the actual response it got from FileMa
288
521
 
289
522
  So, for an annoying, but detailed load of output, make a connection like this:
290
523
 
291
- my_server => Rfm::Server.new(
292
- :host => 'myservername',
293
- :account_name => 'user',
294
- :password => 'pw',
295
- :log_actions => true,
296
- :log_responses => true
297
- )
524
+ my_server => Rfm::Server.new(
525
+ :host => 'myservername',
526
+ :account_name => 'user',
527
+ :password => 'pw',
528
+ :log_actions => true,
529
+ :log_responses => true
530
+ )
531
+
532
+
533
+ ## Credits
534
+
535
+ Rfm was primarily designed by Six Fried Rice co-founder Geoff Coffey.
536
+
537
+ Other lead contributors:
538
+
539
+ * Mufaddal Khumri helped architect Rfm in the most ruby-like way possible. He also contributed the outstanding error handling code and a comprehensive hierarchy of error classes.
540
+ * Atsushi Matsuo was an early Rfm tester, and provided outstanding feedback, critical code fixes, and a lot of web exposure.
541
+ * Jesse Antunes helped ensure that Rfm is stable and functional.
542
+ * Larry Sprock added ssl support, switched the xml parser to a much faster Nokogiri, added the rspec testing framework, and refined code architecture.
298
543
 
299
544
  ## Copyright
300
545