lardawge-rfm 1.4.1.2 → 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +37 -0
- data/Gemfile +4 -0
- data/{README.rdoc → README.md} +149 -101
- data/Rakefile +12 -0
- data/lardawge-rfm.gemspec +29 -0
- data/lib/rfm.rb +9 -12
- data/lib/rfm/database.rb +4 -4
- data/lib/rfm/layout.rb +9 -9
- data/lib/rfm/metadata/field.rb +10 -10
- data/lib/rfm/record.rb +21 -9
- data/lib/rfm/server.rb +65 -46
- data/lib/rfm/utilities/factory.rb +4 -4
- data/lib/version.rb +3 -0
- data/spec/rfm/record_spec.rb +12 -4
- data/spec/spec_helper.rb +5 -5
- metadata +116 -62
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
## 1.4.2
|
2
|
+
|
3
|
+
### enhancements
|
4
|
+
* Make nil default on fields with no value.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
record.john #=> ""
|
8
|
+
record.john #=> nil
|
9
|
+
```
|
10
|
+
|
11
|
+
## 1.4.1.2
|
12
|
+
|
13
|
+
### bug fixes
|
14
|
+
* Pointing out why testing is soooooo important when refactoring... Found a bug in getter/setter method in Rfm::Record
|
15
|
+
|
16
|
+
## 1.4.1.1
|
17
|
+
|
18
|
+
### bug fixes
|
19
|
+
* Inadvertently left out an attr_reader for server from resultset effecting container urls.
|
20
|
+
|
21
|
+
## 1.4.1
|
22
|
+
|
23
|
+
### enhancements
|
24
|
+
* Changed Server#do_action to Server#connect.
|
25
|
+
* XML Parsing is now done via xpath which significantly speeds up parsing.
|
26
|
+
* 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.
|
27
|
+
* Added an option to load portal records which defaults to false. This significantly speeds up load time when portals are present on the layout.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# This will fetch all records with portal records attached.
|
31
|
+
result = fm_server('layout').find({:username => "==#{username}"}, {:include_portals => true})
|
32
|
+
|
33
|
+
result.first.portals # return an empty hash if incude_portals is not true
|
34
|
+
```
|
35
|
+
|
36
|
+
* 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.
|
37
|
+
* 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/Gemfile
ADDED
data/{README.rdoc → README.md}
RENAMED
@@ -1,211 +1,259 @@
|
|
1
|
-
|
1
|
+
# Rfm
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
[![Build Status](https://travis-ci.org/lardawge/rfm.png?branch=master)](https://travis-ci.org/lardawge/rfm)
|
4
|
+
[![Code Quality](https://codeclimate.com/badge.png)](https://codeclimate.com/github/lardawge/rfm)
|
5
|
+
[![Still Maintained](http://stillmaintained.com/lardawge/rfm.png)](http://stillmaintained.com/lardawge/rfm)
|
6
6
|
|
7
|
-
|
7
|
+
## Installation
|
8
8
|
|
9
|
-
|
9
|
+
Terminal:
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
- Larry Sprock added ssl support and switched the xml parser to a much faster Nokogiri.
|
11
|
+
```bash
|
12
|
+
gem install lardawge-rfm
|
13
|
+
```
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
== Installation
|
19
|
-
|
20
|
-
Run the following if you haven't already:
|
21
|
-
|
22
|
-
gem sources -a http://gemcutter.org
|
23
|
-
|
24
|
-
Followed by:
|
15
|
+
Once the gem is installed, you can use rfm in your ruby scripts by requiring it:
|
25
16
|
|
26
|
-
|
17
|
+
```ruby
|
18
|
+
require 'rubygems'
|
19
|
+
require 'rfm'
|
20
|
+
```
|
21
|
+
### In Rails >= 3.0
|
27
22
|
|
28
|
-
|
23
|
+
In the Gemfile:
|
29
24
|
|
30
|
-
|
31
|
-
|
25
|
+
```ruby
|
26
|
+
gem 'lardawge-rfm'
|
27
|
+
```
|
32
28
|
|
33
|
-
|
29
|
+
## Connecting
|
34
30
|
|
35
31
|
IMPORTANT:SSL and Certificate verification are on by default. Please see Server#new in rdocs for explanation and setup.
|
36
32
|
You connect with the Rfm::Server object. This little buddy will be your window into FileMaker data.
|
37
33
|
|
38
|
-
|
34
|
+
```ruby
|
35
|
+
require 'rfm/rfm'
|
39
36
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
37
|
+
my_server = Rfm::Server.new(
|
38
|
+
:host => 'myservername',
|
39
|
+
:username => 'user',
|
40
|
+
:password => 'pw',
|
41
|
+
:ssl => false
|
42
|
+
)
|
43
|
+
```
|
46
44
|
|
47
45
|
if your web publishing engine runs on a port other than 80, you can provide the port number as well:
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
47
|
+
```ruby
|
48
|
+
my_server = Rfm::Server.new(
|
49
|
+
:host => 'myservername',
|
50
|
+
:username => 'user',
|
51
|
+
:password => 'pw',
|
52
|
+
:port => 8080,
|
53
|
+
:ssl => false
|
54
|
+
)
|
55
|
+
```
|
56
56
|
|
57
|
-
|
57
|
+
## Databases and Layouts
|
58
58
|
|
59
59
|
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:
|
60
60
|
|
61
|
-
|
61
|
+
```ruby
|
62
|
+
my_db = my_server.db["My Database"]
|
63
|
+
```
|
62
64
|
|
63
65
|
As a convenience, you can do this too:
|
64
66
|
|
65
|
-
|
67
|
+
```ruby
|
68
|
+
my_db = my_server["My Database"]
|
69
|
+
```
|
66
70
|
|
67
71
|
Finally, if you want to introspect the server and find out what databases are available, you can do this:
|
68
72
|
|
69
|
-
|
73
|
+
```ruby
|
74
|
+
all_dbs = my_server.db.all
|
75
|
+
```
|
70
76
|
|
71
77
|
In any case, you get back Rfm::Database objects. A database object in turn has a property called "layout":
|
72
78
|
|
73
|
-
|
79
|
+
```ruby
|
80
|
+
my_layout = my_db.layout["My Layout"]
|
81
|
+
```
|
74
82
|
|
75
83
|
Again, for convenience:
|
76
84
|
|
77
|
-
|
85
|
+
```ruby
|
86
|
+
my_layout = my_db["My Layout"]
|
87
|
+
```
|
78
88
|
|
79
89
|
And to get them all:
|
80
90
|
|
81
|
-
|
91
|
+
```ruby
|
92
|
+
all_layouts = my_db.layout.all
|
93
|
+
```
|
82
94
|
|
83
95
|
Bringing it all together, you can do this to go straight from a server to a specific layout:
|
84
96
|
|
85
|
-
|
97
|
+
```ruby
|
98
|
+
my_layout = my_server["My Database"]["My Layout"]
|
99
|
+
```
|
86
100
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
== Working with Layouts
|
101
|
+
## Working with Layouts
|
91
102
|
|
92
103
|
Once you have a layout object, you can start doing some real work. To get every record from the layout:
|
93
104
|
|
94
|
-
|
105
|
+
```ruby
|
106
|
+
my_layout.all # be careful with this
|
107
|
+
```
|
95
108
|
|
96
109
|
To get a random record:
|
97
110
|
|
98
|
-
|
111
|
+
```ruby
|
112
|
+
my_layout.any
|
113
|
+
```
|
99
114
|
|
100
115
|
To find every record with "Arizona" in the "State" field:
|
101
116
|
|
102
|
-
|
117
|
+
```ruby
|
118
|
+
my_layout.find({"State" => "Arizona"})
|
119
|
+
```
|
103
120
|
|
104
121
|
To add a new record with my personal info:
|
105
122
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
123
|
+
```ruby
|
124
|
+
my_layout.create({
|
125
|
+
:first_name => "Geoff",
|
126
|
+
:last_name => "Coffey",
|
127
|
+
:email => "gwcoffey@gmail.com"}
|
128
|
+
)
|
129
|
+
```
|
111
130
|
|
112
131
|
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.
|
113
132
|
|
114
133
|
To edit the record whos recid (filemaker internal record id) is 200:
|
115
134
|
|
116
|
-
|
135
|
+
```ruby
|
136
|
+
my_layout.edit(200, {:first_name => 'Mamie'})
|
137
|
+
```
|
117
138
|
|
118
139
|
Note: See the "Record Objects" section below for more on editing records.
|
119
140
|
|
120
141
|
To delete the record whose recid is 200:
|
121
142
|
|
122
|
-
|
143
|
+
```ruby
|
144
|
+
my_layout.delete(200)
|
145
|
+
```
|
123
146
|
|
124
147
|
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:
|
125
148
|
|
126
|
-
|
149
|
+
```ruby
|
150
|
+
my_layout.find({:state => "AZ"}, {:max_records => 10, :skip_records => 100})
|
151
|
+
```
|
127
152
|
|
128
153
|
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.
|
129
154
|
|
130
155
|
Finally, if filemaker returns an error when executing any of these methods, an error will be raised in your ruby script. There is one exception to this, though. If a find results in no records being found (FileMaker error # 401) I just ignore it and return you a ResultSet with zero records in it. If you prefer an error in this case, add :raise_on_401 => true to the options you pass the Rfm::Server when you create it.
|
131
156
|
|
132
157
|
|
133
|
-
|
158
|
+
## ResultSet and Record Objects
|
134
159
|
|
135
160
|
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:
|
136
161
|
|
137
|
-
|
138
|
-
|
139
|
-
|
162
|
+
```ruby
|
163
|
+
my_result = my_layout.any
|
164
|
+
my_result.size # returns '1'
|
165
|
+
my_result[0] # returns the first record (an Rfm::Result::Record object)
|
166
|
+
```
|
140
167
|
|
141
168
|
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:
|
142
169
|
|
143
|
-
|
170
|
+
```ruby
|
171
|
+
my_result.fields.each { |name, field| puts name }
|
172
|
+
```
|
144
173
|
|
145
174
|
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.
|
146
175
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
}
|
176
|
+
```ruby
|
177
|
+
my_result.portals.each { |table, fields|
|
178
|
+
puts "table: #{table}"
|
179
|
+
fields.each { |name, field| puts "\t#{name}"}
|
180
|
+
}
|
181
|
+
```
|
151
182
|
|
152
183
|
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:
|
153
184
|
|
154
|
-
|
155
|
-
|
185
|
+
```ruby
|
186
|
+
my_record = my_result[0]
|
187
|
+
puts my_record["first_name"]
|
188
|
+
```
|
156
189
|
|
157
190
|
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:
|
158
191
|
|
159
|
-
|
192
|
+
```ruby
|
193
|
+
puts my_record.first_name
|
194
|
+
```
|
160
195
|
|
161
196
|
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:
|
162
197
|
|
163
|
-
|
198
|
+
```ruby
|
199
|
+
my_layout.find(:state => 'CA').collect {|rec| "#{rec.first_name} #{rec.last_name}"}.join(", ")
|
200
|
+
```
|
164
201
|
|
165
202
|
Record objects can also be edited:
|
166
203
|
|
167
|
-
|
204
|
+
```ruby
|
205
|
+
my_record.first_name = 'Isabel'
|
206
|
+
```
|
168
207
|
|
169
208
|
Once you have made a series of edits, you can save them back to the database like this:
|
170
209
|
|
171
|
-
|
210
|
+
```ruby
|
211
|
+
my_record.save
|
212
|
+
```
|
172
213
|
|
173
214
|
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.
|
174
215
|
|
175
216
|
If you want to detect concurrent modification, you can do this instead:
|
176
217
|
|
177
|
-
|
218
|
+
```ruby
|
219
|
+
my_record.save_if_not_modified
|
220
|
+
```
|
178
221
|
|
179
222
|
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.
|
180
223
|
|
181
224
|
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:
|
182
225
|
|
183
|
-
|
184
|
-
|
226
|
+
```ruby
|
227
|
+
my_order = order_layout.any[0] # the [0] is important!
|
228
|
+
my_lines = my_order.portals["Line Items"]
|
229
|
+
```
|
185
230
|
|
186
231
|
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.
|
187
232
|
|
188
233
|
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:
|
189
234
|
|
190
|
-
|
191
|
-
|
235
|
+
```ruby
|
236
|
+
my_lines[0]["Quantity"]
|
237
|
+
my_lines[0]["Price"]
|
238
|
+
```
|
192
239
|
|
193
240
|
You would NOT do this:
|
194
241
|
|
195
|
-
|
196
|
-
|
242
|
+
```ruby
|
243
|
+
my_lines[0]["Line Items::Quantity"]
|
244
|
+
my_lines[0]["Line Items::Quantity"]
|
245
|
+
```
|
197
246
|
|
198
247
|
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.
|
199
248
|
|
200
249
|
Again, you can string things together with Ruby. This will calculate the total dollar amount of the order:
|
201
|
-
|
202
|
-
total = 0.0
|
203
|
-
my_order.portals["Line Items"].each {|line| total += line.quantity * line.price}
|
204
250
|
|
205
|
-
|
251
|
+
```ruby
|
252
|
+
total = 0.0
|
253
|
+
my_order.portals["Line Items"].each {|line| total += line.quantity * line.price}
|
254
|
+
```
|
206
255
|
|
207
|
-
|
208
|
-
== Data Types
|
256
|
+
## Data Types
|
209
257
|
|
210
258
|
FileMaker's field types are coerced to Ruby types thusly:
|
211
259
|
|
@@ -228,7 +276,7 @@ Finally, container fields will come back as URI objects. You can:
|
|
228
276
|
|
229
277
|
Specifically, the URI refers to the _contents_ of the container field. When accessed, the file, picture, or movie in the field will be downloaded.
|
230
278
|
|
231
|
-
|
279
|
+
## Troubleshooting
|
232
280
|
|
233
281
|
There are two cheesy methods to help track down problems. When you create a server object, you can provide two additional optional parameters:
|
234
282
|
|
@@ -240,16 +288,16 @@ When this is 'true' your script will dump the actual response it got from FileMa
|
|
240
288
|
|
241
289
|
So, for an annoying, but detailed load of output, make a connection like this:
|
242
290
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
291
|
+
```ruby
|
292
|
+
my_server # Rfm::Server.new(
|
293
|
+
:host #> 'myservername',
|
294
|
+
:username #> 'user',
|
295
|
+
:password #> 'pw',
|
296
|
+
:log_actions #> true,
|
297
|
+
:log_responses #> true
|
298
|
+
)
|
299
|
+
```
|
252
300
|
|
253
|
-
|
301
|
+
## Copyright
|
254
302
|
|
255
|
-
Copyright (c) 2007 Six Fried Rice, LLC and Mufaddal Khumr. See LICENSE for details.
|
303
|
+
Copyright (c) 2007 Six Fried Rice, LLC and Mufaddal Khumr. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'bundler'
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
desc "Run all examples"
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
9
|
+
t.pattern = 'spec/**/*_spec.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :spec
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "lardawge-rfm"
|
7
|
+
s.version = Rfm::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Geoff Coffey", "Mufaddal Khumri", "Atsushi Matsuo", "Larry Sprock"]
|
10
|
+
s.email = ["larry@lucidbleu.com"]
|
11
|
+
s.homepage = "https://github.com/lardawge/rfm"
|
12
|
+
s.licenses = ["MIT"]
|
13
|
+
s.summary = %q{Ruby to Filemaker adapter}
|
14
|
+
s.description = %q{Rfm brings your FileMaker data to Ruby. Now your Ruby scripts and Rails applications can talk directly to your FileMaker server.}
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "nokogiri"
|
22
|
+
s.add_dependency "addressable"
|
23
|
+
|
24
|
+
s.add_development_dependency "rspec", ["~> 2.12.0"]
|
25
|
+
s.add_development_dependency "mocha", ["~> 0.13.0"]
|
26
|
+
s.add_development_dependency "rake"
|
27
|
+
|
28
|
+
end
|
29
|
+
|
data/lib/rfm.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require
|
5
|
-
require
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rfm/utilities/case_insensitive_hash'
|
3
|
+
require 'rfm/utilities/factory'
|
4
|
+
require 'rfm/error'
|
5
|
+
require 'rfm/server'
|
6
|
+
require 'rfm/database'
|
7
|
+
require 'rfm/layout'
|
8
|
+
require 'rfm/resultset'
|
6
9
|
|
7
10
|
module Rfm
|
8
11
|
|
@@ -10,10 +13,4 @@ module Rfm
|
|
10
13
|
class ParameterError < StandardError; end
|
11
14
|
class AuthenticationError < StandardError; end
|
12
15
|
|
13
|
-
|
14
|
-
autoload :Server, 'rfm/server'
|
15
|
-
autoload :Database, 'rfm/database'
|
16
|
-
autoload :Layout, 'rfm/layout'
|
17
|
-
autoload :Resultset, 'rfm/resultset'
|
18
|
-
|
19
|
-
end
|
16
|
+
end
|
data/lib/rfm/database.rb
CHANGED
@@ -59,6 +59,9 @@ module Rfm
|
|
59
59
|
# * *state* is a hash of all server options used to initialize this server
|
60
60
|
class Database
|
61
61
|
|
62
|
+
attr_reader :server, :name, :account_name, :password, :layout, :script
|
63
|
+
attr_writer :account_name, :password
|
64
|
+
|
62
65
|
# Initialize a database object. You never really need to do this. Instead, just do this:
|
63
66
|
#
|
64
67
|
# myServer = Rfm::Server.new(...)
|
@@ -74,9 +77,6 @@ module Rfm
|
|
74
77
|
@script = Rfm::Factory::ScriptFactory.new(server, self)
|
75
78
|
end
|
76
79
|
|
77
|
-
attr_reader :server, :name, :account_name, :password, :layout, :script
|
78
|
-
attr_writer :account_name, :password
|
79
|
-
|
80
80
|
# Access the Layout object representing a layout in this database. For example:
|
81
81
|
#
|
82
82
|
# myDatabase['Details']
|
@@ -93,4 +93,4 @@ module Rfm
|
|
93
93
|
end
|
94
94
|
|
95
95
|
end
|
96
|
-
end
|
96
|
+
end
|
data/lib/rfm/layout.rb
CHANGED
@@ -120,6 +120,8 @@ module Rfm
|
|
120
120
|
|
121
121
|
class Layout
|
122
122
|
|
123
|
+
attr_reader :name, :db
|
124
|
+
|
123
125
|
# Initialize a layout object. You never really need to do this. Instead, just do this:
|
124
126
|
#
|
125
127
|
# myServer = Rfm::Server.new(...)
|
@@ -138,8 +140,6 @@ module Rfm
|
|
138
140
|
@db = db
|
139
141
|
end
|
140
142
|
|
141
|
-
attr_reader :name, :db
|
142
|
-
|
143
143
|
# Returns a ResultSet object containing _every record_ in the table associated with this layout.
|
144
144
|
def all(options = {})
|
145
145
|
get_records('-findall', {}, options)
|
@@ -209,14 +209,14 @@ module Rfm
|
|
209
209
|
|
210
210
|
private
|
211
211
|
|
212
|
-
def get_records(action,
|
213
|
-
include_portals = options
|
214
|
-
xml_response =
|
215
|
-
Rfm::Resultset.new(
|
212
|
+
def get_records(action, params = {}, options = {})
|
213
|
+
include_portals = options.delete(:include_portals)
|
214
|
+
xml_response = db.server.connect(action, default_params.merge(params), options)
|
215
|
+
Rfm::Resultset.new(db.server, xml_response.body, self, include_portals)
|
216
216
|
end
|
217
217
|
|
218
|
-
def
|
219
|
-
{"-db" =>
|
218
|
+
def default_params
|
219
|
+
{"-db" => db.name, "-lay" => self.name}
|
220
220
|
end
|
221
221
|
end
|
222
|
-
end
|
222
|
+
end
|
data/lib/rfm/metadata/field.rb
CHANGED
@@ -45,7 +45,7 @@ module Rfm
|
|
45
45
|
#
|
46
46
|
# * *control& is a FieldControl object representing the sytle and value list information associated
|
47
47
|
# with this field on the layout.
|
48
|
-
#
|
48
|
+
#
|
49
49
|
# Note: Since a field can sometimes appear on a layout more than once, +control+ may be an Array.
|
50
50
|
# If you don't know ahead of time, you'll need to deal with this. One easy way is:
|
51
51
|
#
|
@@ -56,11 +56,11 @@ module Rfm
|
|
56
56
|
#
|
57
57
|
# The code above makes sure the control is always an array. Typically, though, you'll know up front
|
58
58
|
# if the control is an array or not, and you can code accordingly.
|
59
|
-
|
59
|
+
|
60
60
|
class Field
|
61
|
-
|
61
|
+
|
62
62
|
attr_reader :name, :result, :type, :max_repeats, :global
|
63
|
-
|
63
|
+
|
64
64
|
# Initializes a field object. You'll never need to do this. Instead, get your Field objects from
|
65
65
|
# ResultSet::fields
|
66
66
|
def initialize(field)
|
@@ -70,8 +70,8 @@ module Rfm
|
|
70
70
|
@max_repeats = field['max-repeats']
|
71
71
|
@global = field['global']
|
72
72
|
end
|
73
|
-
|
74
|
-
# Coerces the text value from an +fmresultset+ document into proper Ruby types based on the
|
73
|
+
|
74
|
+
# Coerces the text value from an +fmresultset+ document into proper Ruby types based on the
|
75
75
|
# type of the field. You'll never need to do this: Rfm does it automatically for you when you
|
76
76
|
# access field data through the Record object.
|
77
77
|
def coerce(value, resultset)
|
@@ -82,12 +82,12 @@ module Rfm
|
|
82
82
|
when "date" then Date.strptime(value, resultset.date_format)
|
83
83
|
when "time" then DateTime.strptime("1/1/-4712 #{value}", "%m/%d/%Y #{resultset.time_format}")
|
84
84
|
when "timestamp" then DateTime.strptime(value, resultset.timestamp_format)
|
85
|
-
when "container" then URI.parse("#{resultset.server.scheme}://#{resultset.server.
|
85
|
+
when "container" then URI.parse("#{resultset.server.uri.scheme}://#{resultset.server.uri.host}:#{resultset.server.uri.port}#{value}")
|
86
86
|
else nil
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
end
|
92
92
|
end
|
93
|
-
end
|
93
|
+
end
|
data/lib/rfm/record.rb
CHANGED
@@ -191,26 +191,38 @@ module Rfm
|
|
191
191
|
# Record::save or Record::save_if_not_modified to actually save the data.
|
192
192
|
def []=(name, value)
|
193
193
|
return super unless @loaded
|
194
|
-
raise Rfm::ParameterError,
|
194
|
+
raise Rfm::ParameterError,
|
195
|
+
"You attempted to modify the field :#{name} which does not exist in the current Filemaker layout." unless self.key?(name)
|
195
196
|
@mods[name] = value
|
196
197
|
end
|
197
|
-
|
198
|
+
|
199
|
+
alias :_original_hash_reader :[]
|
200
|
+
def [](value)
|
201
|
+
read_attribute(value)
|
202
|
+
end
|
203
|
+
|
198
204
|
def respond_to?(symbol, include_private = false)
|
199
|
-
return true if self
|
205
|
+
return true if self.include?(symbol.to_s)
|
200
206
|
super
|
201
207
|
end
|
202
|
-
|
208
|
+
|
203
209
|
private
|
204
|
-
|
205
|
-
def
|
210
|
+
|
211
|
+
def read_attribute(key)
|
212
|
+
raise NoMethodError,
|
213
|
+
"#{key} does not exist as a field in the current Filemaker layout." unless key?(key)
|
214
|
+
_original_hash_reader(key)
|
215
|
+
end
|
216
|
+
|
217
|
+
def method_missing(symbol, *attrs, &block)
|
206
218
|
method = symbol.to_s
|
207
|
-
return
|
219
|
+
return read_attribute(method) if self.key?(method)
|
208
220
|
|
209
|
-
if method =~ /(=)$/ && self.
|
221
|
+
if method =~ /(=)$/ && self.key?($`)
|
210
222
|
return @mods[$`] = attrs.first
|
211
223
|
end
|
212
224
|
super
|
213
225
|
end
|
214
226
|
|
215
227
|
end
|
216
|
-
end
|
228
|
+
end
|
data/lib/rfm/server.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'net/https'
|
2
|
-
require '
|
2
|
+
require 'addressable/uri'
|
3
|
+
|
3
4
|
module Rfm
|
4
5
|
# This class represents a single FileMaker server. It is initialized with basic
|
5
6
|
# connection information, including the hostname, port number, and default database
|
@@ -107,7 +108,9 @@ module Rfm
|
|
107
108
|
# * *name* is the name of this database
|
108
109
|
# * *state* is a hash of all server options used to initialize this server
|
109
110
|
class Server
|
110
|
-
|
111
|
+
|
112
|
+
attr_reader :db, :state, :uri
|
113
|
+
|
111
114
|
# To create a Server object, you typically need at least a host name:
|
112
115
|
#
|
113
116
|
# myServer = Rfm::Server.new({:host => 'my.host.com'})
|
@@ -192,7 +195,6 @@ module Rfm
|
|
192
195
|
# :root_cert_name => 'example.pem'
|
193
196
|
# :root_cert_path => '/usr/cert_file/'
|
194
197
|
# })
|
195
|
-
|
196
198
|
def initialize(options)
|
197
199
|
@state = {
|
198
200
|
:host => 'localhost',
|
@@ -203,21 +205,16 @@ module Rfm
|
|
203
205
|
:root_cert_path => '/',
|
204
206
|
:account_name => '',
|
205
207
|
:password => '',
|
206
|
-
:log_actions =>
|
207
|
-
:log_responses =>
|
208
|
+
:log_actions => nil,
|
209
|
+
:log_responses => nil,
|
208
210
|
:warn_on_redirect => true,
|
209
|
-
:raise_on_401 =>
|
211
|
+
:raise_on_401 => nil
|
210
212
|
}.merge(options)
|
211
|
-
|
212
|
-
@state
|
213
|
-
|
214
|
-
@host_name = @state[:host]
|
215
|
-
@scheme = @state[:ssl] ? "https" : "http"
|
216
|
-
@port = @state[:ssl] && options[:port].nil? ? 443 : @state[:port]
|
217
|
-
|
218
|
-
@db = Rfm::Factory::DbFactory.new(self)
|
213
|
+
|
214
|
+
@uri = Addressable::URI.parse("#{scheme}://#{state[:host]}:#{port}")
|
215
|
+
@db = Rfm::Factory::DbFactory.new(self)
|
219
216
|
end
|
220
|
-
|
217
|
+
|
221
218
|
# Access the database object representing a database on the server. For example:
|
222
219
|
#
|
223
220
|
# myServer['Customers']
|
@@ -233,9 +230,7 @@ module Rfm
|
|
233
230
|
def [](dbname)
|
234
231
|
self.db[dbname]
|
235
232
|
end
|
236
|
-
|
237
|
-
attr_reader :db, :host_name, :port, :scheme, :state
|
238
|
-
|
233
|
+
|
239
234
|
# Performs a raw FileMaker action. You will generally not call this method directly, but it
|
240
235
|
# is exposed in case you need to do something "under the hood."
|
241
236
|
#
|
@@ -263,70 +258,78 @@ module Rfm
|
|
263
258
|
# },
|
264
259
|
# { :max_records => 20 }
|
265
260
|
# )
|
266
|
-
def connect(
|
261
|
+
def connect(action, args, options = {})
|
267
262
|
post = args.merge(expand_options(options)).merge({action => ''})
|
268
|
-
http_fetch(
|
263
|
+
http_fetch("/fmi/xml/fmresultset.xml", post)
|
269
264
|
end
|
270
|
-
|
265
|
+
|
271
266
|
def load_layout(layout)
|
272
267
|
post = {'-db' => layout.db.name, '-lay' => layout.name, '-view' => ''}
|
273
|
-
http_fetch(
|
268
|
+
http_fetch("/fmi/xml/FMPXMLLAYOUT.xml", post)
|
274
269
|
end
|
275
|
-
|
270
|
+
|
276
271
|
private
|
277
|
-
|
278
|
-
def http_fetch(
|
272
|
+
|
273
|
+
def http_fetch(path, post_data, limit=10)
|
279
274
|
raise Rfm::CommunicationError.new("While trying to reach the Web Publishing Engine, RFM was redirected too many times.") if limit == 0
|
280
275
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
end
|
276
|
+
uri.path = path
|
277
|
+
uri.query_values = post_data
|
278
|
+
warn uri.to_s if state[:log_actions]
|
285
279
|
|
286
|
-
request = Net::HTTP::Post.new(path)
|
287
|
-
request.basic_auth(account_name, password)
|
280
|
+
request = Net::HTTP::Post.new(uri.path)
|
281
|
+
request.basic_auth(state[:account_name], state[:password])
|
288
282
|
request.set_form_data(post_data)
|
289
283
|
|
290
|
-
response = Net::HTTP.new(
|
284
|
+
response = Net::HTTP.new(uri.host, uri.port)
|
291
285
|
|
292
|
-
if
|
286
|
+
if state[:ssl]
|
293
287
|
response.use_ssl = true
|
294
|
-
if
|
288
|
+
if state[:root_cert]
|
295
289
|
response.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
296
|
-
response.ca_file = File.join(
|
290
|
+
response.ca_file = File.join(state[:root_cert_path], state[:root_cert_name])
|
297
291
|
else
|
298
292
|
response.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
299
293
|
end
|
300
294
|
end
|
301
295
|
|
302
296
|
response = response.start { |http| http.request(request) }
|
303
|
-
if
|
297
|
+
if state[:log_responses]
|
304
298
|
response.to_hash.each { |key, value| warn "#{key}: #{value}" }
|
305
299
|
warn response.body
|
306
300
|
end
|
307
|
-
|
301
|
+
|
302
|
+
parse_response(response, limit)
|
303
|
+
end
|
304
|
+
|
305
|
+
def parse_response(response, limit)
|
308
306
|
case response
|
309
307
|
when Net::HTTPSuccess
|
310
308
|
response
|
311
309
|
when Net::HTTPRedirection
|
312
|
-
if
|
313
|
-
warn "The web server redirected to
|
314
|
-
|
310
|
+
if state[:warn_on_redirect]
|
311
|
+
warn "The web server redirected to #{response['location']}.
|
312
|
+
You should revise your connection hostname or fix your server configuration if possible to improve performance."
|
315
313
|
end
|
316
|
-
|
317
|
-
|
314
|
+
|
315
|
+
newloc = Addressable::URI.parse(response['location'])
|
316
|
+
uri.host = newloc.host
|
317
|
+
uri.port = newloc.port
|
318
|
+
http_fetch(newloc.path, newloc.query_values, limit-1)
|
318
319
|
when Net::HTTPUnauthorized
|
319
|
-
msg = "The account name (#{account_name}) or password provided is not correct
|
320
|
+
msg = "The account name (#{state[:account_name]}) or password provided is not correct
|
321
|
+
(or the account doesn't have the fmxml extended privilege)."
|
320
322
|
raise Rfm::AuthenticationError.new(msg)
|
321
323
|
when Net::HTTPNotFound
|
322
324
|
msg = "Could not talk to FileMaker because the Web Publishing Engine is not responding (server returned 404)."
|
323
325
|
raise Rfm::CommunicationError.new(msg)
|
324
326
|
else
|
325
|
-
msg = "Unexpected response from server: #{response.code} (#{response.class.to_s}).
|
327
|
+
msg = "Unexpected response from server: #{response.code} (#{response.class.to_s}).
|
328
|
+
Unable to communicate with the Web Publishing Engine."
|
326
329
|
raise Rfm::CommunicationError.new(msg)
|
327
330
|
end
|
328
331
|
end
|
329
|
-
|
332
|
+
|
330
333
|
def expand_options(options)
|
331
334
|
result = {}
|
332
335
|
options.each do |key,value|
|
@@ -382,6 +385,22 @@ module Rfm
|
|
382
385
|
end
|
383
386
|
return result
|
384
387
|
end
|
388
|
+
|
389
|
+
def scheme
|
390
|
+
if state[:ssl]
|
391
|
+
"https"
|
392
|
+
else
|
393
|
+
"http"
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def port
|
398
|
+
if state[:ssl] && state[:port] == 80
|
399
|
+
443
|
400
|
+
else
|
401
|
+
state[:port]
|
402
|
+
end
|
403
|
+
end
|
385
404
|
|
386
405
|
end
|
387
|
-
end
|
406
|
+
end
|
@@ -20,7 +20,7 @@ module Rfm
|
|
20
20
|
|
21
21
|
def all
|
22
22
|
if !@loaded
|
23
|
-
Rfm::Result::ResultSet.new(@server, @server.connect(
|
23
|
+
Rfm::Result::ResultSet.new(@server, @server.connect('-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
|
}
|
@@ -45,7 +45,7 @@ module Rfm
|
|
45
45
|
|
46
46
|
def all
|
47
47
|
if !@loaded
|
48
|
-
Rfm::Result::ResultSet.new(@server, @server.connect(
|
48
|
+
Rfm::Result::ResultSet.new(@server, @server.connect('-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
|
}
|
@@ -70,7 +70,7 @@ module Rfm
|
|
70
70
|
|
71
71
|
def all
|
72
72
|
if !@loaded
|
73
|
-
Rfm::Result::ResultSet.new(@server, @server.connect(
|
73
|
+
Rfm::Result::ResultSet.new(@server, @server.connect('-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
|
}
|
@@ -81,4 +81,4 @@ module Rfm
|
|
81
81
|
|
82
82
|
end
|
83
83
|
end
|
84
|
-
end
|
84
|
+
end
|
data/lib/version.rb
ADDED
data/spec/rfm/record_spec.rb
CHANGED
@@ -24,12 +24,20 @@ describe Rfm::Record do
|
|
24
24
|
@record.instance_variable_get(:@mods)['tester'].should eql('green')
|
25
25
|
end
|
26
26
|
|
27
|
-
it "raises an Rfm::ParameterError if a
|
27
|
+
it "raises an Rfm::ParameterError if a value is set on a key that does not exist" do
|
28
28
|
@record.instance_variable_set(:@loaded, true)
|
29
|
-
|
29
|
+
|
30
30
|
ex = rescue_from { @record['tester2'] = 'error' }
|
31
31
|
ex.class.should eql(Rfm::ParameterError)
|
32
|
-
ex.message.should eql('You attempted to modify
|
32
|
+
ex.message.should eql('You attempted to modify the field :tester2 which does not exist in the current Filemaker layout.')
|
33
|
+
end
|
34
|
+
|
35
|
+
it "raises an NoMethodError if a key is used that does not exist" do
|
36
|
+
@record.instance_variable_set(:@loaded, true)
|
37
|
+
|
38
|
+
ex = rescue_from { @record['tester2'] }
|
39
|
+
ex.class.should eql(NoMethodError)
|
40
|
+
ex.message.should eql('tester2 does not exist as a field in the current Filemaker layout.')
|
33
41
|
end
|
34
42
|
|
35
43
|
end
|
@@ -80,4 +88,4 @@ describe Rfm::Record do
|
|
80
88
|
end
|
81
89
|
|
82
90
|
end
|
83
|
-
end
|
91
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'logger'
|
3
4
|
require 'rfm'
|
4
|
-
require 'spec'
|
5
|
-
require 'spec/autorun'
|
6
5
|
|
7
|
-
|
6
|
+
RSpec.configure do |c|
|
7
|
+
c.mock_with :mocha
|
8
8
|
end
|
9
9
|
|
10
10
|
def rescue_from(&block)
|
metadata
CHANGED
@@ -1,16 +1,10 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: lardawge-rfm
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 4
|
9
|
-
- 1
|
10
|
-
- 2
|
11
|
-
version: 1.4.1.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.4.2
|
5
|
+
prerelease:
|
12
6
|
platform: ruby
|
13
|
-
authors:
|
7
|
+
authors:
|
14
8
|
- Geoff Coffey
|
15
9
|
- Mufaddal Khumri
|
16
10
|
- Atsushi Matsuo
|
@@ -18,34 +12,105 @@ authors:
|
|
18
12
|
autorequire:
|
19
13
|
bindir: bin
|
20
14
|
cert_chain: []
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
dependencies:
|
25
|
-
- !ruby/object:Gem::Dependency
|
15
|
+
date: 2013-06-02 00:00:00.000000000 Z
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
26
18
|
name: nokogiri
|
19
|
+
requirement: !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ! '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '0'
|
25
|
+
type: :runtime
|
27
26
|
prerelease: false
|
28
|
-
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: addressable
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
29
36
|
none: false
|
30
|
-
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
|
34
|
-
segments:
|
35
|
-
- 0
|
36
|
-
version: "0"
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
37
41
|
type: :runtime
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rspec
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ~>
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 2.12.0
|
57
|
+
type: :development
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ~>
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 2.12.0
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: mocha
|
67
|
+
requirement: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ~>
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 0.13.0
|
73
|
+
type: :development
|
74
|
+
prerelease: false
|
75
|
+
version_requirements: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ~>
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 0.13.0
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rake
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Rfm brings your FileMaker data to Ruby. Now your Ruby scripts and Rails
|
98
|
+
applications can talk directly to your FileMaker server.
|
99
|
+
email:
|
100
|
+
- larry@lucidbleu.com
|
41
101
|
executables: []
|
42
|
-
|
43
102
|
extensions: []
|
44
|
-
|
45
|
-
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- .gitignore
|
106
|
+
- .rspec
|
107
|
+
- .travis.yml
|
108
|
+
- CHANGELOG.md
|
109
|
+
- Gemfile
|
46
110
|
- LICENSE
|
47
|
-
- README.
|
48
|
-
|
111
|
+
- README.md
|
112
|
+
- Rakefile
|
113
|
+
- lardawge-rfm.gemspec
|
49
114
|
- lib/rfm.rb
|
50
115
|
- lib/rfm/database.rb
|
51
116
|
- lib/rfm/error.rb
|
@@ -57,48 +122,37 @@ files:
|
|
57
122
|
- lib/rfm/server.rb
|
58
123
|
- lib/rfm/utilities/case_insensitive_hash.rb
|
59
124
|
- lib/rfm/utilities/factory.rb
|
60
|
-
-
|
61
|
-
- README.rdoc
|
125
|
+
- lib/version.rb
|
62
126
|
- spec/rfm/error_spec.rb
|
63
127
|
- spec/rfm/record_spec.rb
|
64
128
|
- spec/spec_helper.rb
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
129
|
+
homepage: https://github.com/lardawge/rfm
|
130
|
+
licenses:
|
131
|
+
- MIT
|
69
132
|
post_install_message:
|
70
|
-
rdoc_options:
|
71
|
-
|
72
|
-
- --main
|
73
|
-
- README.rdoc
|
74
|
-
require_paths:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
75
135
|
- lib
|
76
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
137
|
none: false
|
78
|
-
requirements:
|
79
|
-
- -
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
|
82
|
-
|
83
|
-
- 0
|
84
|
-
version: "0"
|
85
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
143
|
none: false
|
87
|
-
requirements:
|
88
|
-
- -
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
version: "0"
|
144
|
+
requirements:
|
145
|
+
- - ! '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
94
148
|
requirements: []
|
95
|
-
|
96
149
|
rubyforge_project:
|
97
|
-
rubygems_version: 1.
|
150
|
+
rubygems_version: 1.8.24
|
98
151
|
signing_key:
|
99
152
|
specification_version: 3
|
100
153
|
summary: Ruby to Filemaker adapter
|
101
|
-
test_files:
|
154
|
+
test_files:
|
102
155
|
- spec/rfm/error_spec.rb
|
103
156
|
- spec/rfm/record_spec.rb
|
104
157
|
- spec/spec_helper.rb
|
158
|
+
has_rdoc:
|