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.
- data/CHANGELOG.md +107 -0
- data/README.md +378 -133
- data/lib/rfm.rb +51 -19
- data/lib/rfm/VERSION +1 -1
- data/lib/rfm/base.rb +416 -0
- data/lib/rfm/database.rb +14 -9
- data/lib/rfm/layout.rb +148 -96
- data/lib/rfm/metadata/field.rb +5 -5
- data/lib/rfm/metadata/field_control.rb +52 -51
- data/lib/rfm/metadata/script.rb +7 -5
- data/lib/rfm/record.rb +71 -56
- data/lib/rfm/resultset.rb +45 -26
- data/lib/rfm/server.rb +21 -17
- data/lib/rfm/utilities/complex_query.rb +64 -0
- data/lib/rfm/utilities/config.rb +115 -0
- data/lib/rfm/utilities/core_ext.rb +90 -0
- data/lib/rfm/utilities/factory.rb +100 -17
- data/lib/rfm/utilities/xml_parser.rb +94 -0
- data/lib/rfm/version.rb +1 -1
- data/lib/rfm/xml_mini/hpricot.rb +133 -0
- metadata +87 -30
data/CHANGELOG.md
ADDED
@@ -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.
|
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
|
-
|
86
|
+
### Configuration API
|
6
87
|
|
7
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
96
|
+
Use RFM_CONFIG
|
15
97
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
108
|
+
Or set global configuration with the 'config' method
|
24
109
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
121
|
+
Rfm::Base.config :ssl => true
|
32
122
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
46
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
324
|
+
require 'rfm'
|
91
325
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
348
|
+
my_db = my_server.db["My Database"]
|
115
349
|
|
116
350
|
As a convenience, you can do this too:
|
117
351
|
|
118
|
-
|
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
|
-
|
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
|
-
|
360
|
+
my_layout = my_db.layout["My Layout"]
|
127
361
|
|
128
362
|
Again, for convenience:
|
129
363
|
|
130
|
-
|
364
|
+
my_layout = my_db["My Layout"]
|
131
365
|
|
132
366
|
And to get them all:
|
133
367
|
|
134
|
-
|
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
|
-
|
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
|
-
|
378
|
+
my_layout.all # be careful with this
|
145
379
|
|
146
380
|
To get a random record:
|
147
381
|
|
148
|
-
|
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
|
-
|
386
|
+
my_layout.find({"State" => "Arizona"})
|
154
387
|
|
155
388
|
To add a new record with my personal info:
|
156
389
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
398
|
+
To edit the record whose recid (filemaker internal record id) is 200:
|
166
399
|
|
167
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
189
|
-
|
190
|
-
|
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
|
-
|
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
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
206
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
235
|
-
|
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
|
-
|
242
|
-
|
474
|
+
my_lines[0]["Quantity"]
|
475
|
+
my_lines[0]["Price"]
|
243
476
|
|
244
477
|
You would NOT do this:
|
245
478
|
|
246
|
-
|
247
|
-
|
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
|
-
|
254
|
-
|
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
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
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
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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
|
|