rfm 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +19 -7
- data/lib/rfm_command.rb +16 -8
- data/lib/rfm_factory.rb +4 -4
- data/lib/rfm_result.rb +30 -13
- metadata +2 -2
data/README
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
This documentation is hastily thrown together and probably contains lots of errors. Needless to say, this will need to be documented better soon :)
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
Installation
|
4
|
+
------------
|
5
5
|
|
6
|
-
|
6
|
+
rfm is availabl as a gem from rubyforge.org, so you can get it like this:
|
7
7
|
|
8
|
-
|
8
|
+
gem install rfm
|
9
|
+
|
10
|
+
Once the gem is installed, you can use rfm in your ruby scripts by requiring it:
|
9
11
|
|
10
|
-
|
12
|
+
require 'rubygems'
|
13
|
+
require 'rfm'
|
11
14
|
|
12
15
|
Connecting
|
13
16
|
----------
|
@@ -106,7 +109,7 @@ All of these methods return an Rfm::Result::ResultSet object (see below), and ev
|
|
106
109
|
|
107
110
|
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.
|
108
111
|
|
109
|
-
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.
|
112
|
+
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.
|
110
113
|
|
111
114
|
|
112
115
|
ResultSet and Record Objects
|
@@ -195,11 +198,20 @@ FileMaker's field types are coerced to Ruby types thusly:
|
|
195
198
|
Date Field -> Date object
|
196
199
|
Time Field -> DateTime object # see below
|
197
200
|
TimeStamp Field -> DateTime object
|
201
|
+
Container Field -> URI object
|
198
202
|
|
199
203
|
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.
|
200
204
|
|
201
205
|
Also, Ruby doesn't have a Time type that stores just a normal time (with no date attached). The Time class in ruby is a lot like DateTime, or a Timestamp in FileMaker. When I get a Time field from FileMaker, I turn it into a DateTime object, and set its date to the oldest date Ruby supports. You can still compare these in all the normal ways, so this should be fine, but it will look weird if you, ie, to_s one and see an odd date attached to your time.
|
202
206
|
|
207
|
+
Finally, container fields will come back as URI objects. You can:
|
208
|
+
|
209
|
+
- use Net::HTTP to download the contents of the container field using this URI
|
210
|
+
- to_s the URI and use it as the src attribute of an HTML image tag
|
211
|
+
- etc...
|
212
|
+
|
213
|
+
Specifically, the URI refers to the _contents_ of the container field. When accessed, the file, picture, or movie in the field will be downloaded.
|
214
|
+
|
203
215
|
Troubleshooting
|
204
216
|
---------------
|
205
217
|
|
@@ -221,4 +233,4 @@ So, for an annoying, but detailed load of output, make a connection like this:
|
|
221
233
|
:log_responses => true
|
222
234
|
)
|
223
235
|
|
224
|
-
These options will change in the future. They're only there now as a quick-easy way for me to track down problems. I'm open to suggestions for what kind of loggin options will make sense in the final release.
|
236
|
+
These options will change in the future. They're only there now as a quick-easy way for me to track down problems. I'm open to suggestions for what kind of loggin options will make sense in the final release.
|
data/lib/rfm_command.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
require 'rexml/document'
|
3
|
+
require 'cgi'
|
3
4
|
|
4
5
|
module Rfm
|
5
6
|
|
@@ -11,9 +12,11 @@ module Rfm
|
|
11
12
|
:port => 80,
|
12
13
|
:username => '',
|
13
14
|
:password => '',
|
14
|
-
:log_actions => false
|
15
|
-
|
16
|
-
|
15
|
+
:log_actions => false,
|
16
|
+
:log_responses => false,
|
17
|
+
:raise_on_401 => false
|
18
|
+
}.merge(options).freeze
|
19
|
+
|
17
20
|
@host_name = @state[:host]
|
18
21
|
@port = @state[:port]
|
19
22
|
@username = @state[:username]
|
@@ -26,13 +29,13 @@ module Rfm
|
|
26
29
|
self.db[dbname]
|
27
30
|
end
|
28
31
|
|
29
|
-
attr_reader :db
|
32
|
+
attr_reader :db, :host_name, :port, :state
|
30
33
|
|
31
34
|
def do_action(action, args, options = {})
|
32
35
|
post = args.merge(expand_options(options)).merge({action => ''})
|
33
36
|
|
34
37
|
if @state[:log_actions] == true
|
35
|
-
qs = post.collect{|key,val| "#{key}=#{val}"}.join("&")
|
38
|
+
qs = post.collect{|key,val| "#{CGI::escape(key.to_s)}=#{CGI::escape(val.to_s)}"}.join("&")
|
36
39
|
warn "http://#{@host_name}:#{@port}/fmi/xml/fmresultset.xml?#{qs}"
|
37
40
|
end
|
38
41
|
|
@@ -44,7 +47,10 @@ module Rfm
|
|
44
47
|
}
|
45
48
|
|
46
49
|
if @state[:log_responses] == true
|
47
|
-
|
50
|
+
result.to_hash.each {|key, value|
|
51
|
+
warn "#{key}: #{value}"
|
52
|
+
}
|
53
|
+
warn result.body
|
48
54
|
end
|
49
55
|
|
50
56
|
result
|
@@ -71,7 +77,7 @@ module Rfm
|
|
71
77
|
else
|
72
78
|
result['-script'] = value
|
73
79
|
end
|
74
|
-
when :
|
80
|
+
when :pre_find_script:
|
75
81
|
if value.class == Array
|
76
82
|
result['-script.prefind'] = value[0]
|
77
83
|
result['-script.prefind.param'] = value[1]
|
@@ -89,6 +95,8 @@ module Rfm
|
|
89
95
|
result['-lay.response'] = value
|
90
96
|
when :logical_operator:
|
91
97
|
result['-lop'] = value
|
98
|
+
when :modification_id:
|
99
|
+
result['-modid'] = value
|
92
100
|
else
|
93
101
|
raise "Invalid option: #{key} (are you using a string instead of a symbol?)"
|
94
102
|
end
|
@@ -151,7 +159,7 @@ module Rfm
|
|
151
159
|
|
152
160
|
def get_records(action, extra_params = {}, options = {})
|
153
161
|
Rfm::Result::ResultSet.new(
|
154
|
-
@db.server.do_action(action, params().merge(extra_params), options).body,
|
162
|
+
@db.server, @db.server.do_action(action, params().merge(extra_params), options).body,
|
155
163
|
self)
|
156
164
|
end
|
157
165
|
|
data/lib/rfm_factory.rb
CHANGED
@@ -12,11 +12,11 @@ module Rfm::Factory
|
|
12
12
|
|
13
13
|
def all
|
14
14
|
if !@loaded
|
15
|
-
Rfm::Result::ResultSet.new(@server.do_action('-dbnames', {}).body).each {|record|
|
15
|
+
Rfm::Result::ResultSet.new(@server, @server.do_action('-dbnames', {}).body).each {|record|
|
16
16
|
name = record['DATABASE_NAME']
|
17
17
|
self[name] = Rfm::Database.new(name, @server) if self[name] == nil
|
18
18
|
}
|
19
|
-
@loaded =
|
19
|
+
@loaded = true
|
20
20
|
end
|
21
21
|
self.values
|
22
22
|
end
|
@@ -37,7 +37,7 @@ module Rfm::Factory
|
|
37
37
|
|
38
38
|
def all
|
39
39
|
if !@loaded
|
40
|
-
Rfm::Result::ResultSet.new(@server.do_action('-layoutnames', {"-db" => @database.name}).body).each {|record|
|
40
|
+
Rfm::Result::ResultSet.new(@server, @server.do_action('-layoutnames', {"-db" => @database.name}).body).each {|record|
|
41
41
|
name = record['LAYOUT_NAME']
|
42
42
|
self[name] = Rfm::Layout.new(name, @database) if self[name] == nil
|
43
43
|
}
|
@@ -62,7 +62,7 @@ module Rfm::Factory
|
|
62
62
|
|
63
63
|
def all
|
64
64
|
if !@loaded
|
65
|
-
Rfm::Result::ResultSet.new(@server.do_action('-scriptnames', {"-db" => @database.name}).body).each {|record|
|
65
|
+
Rfm::Result::ResultSet.new(@server, @server.do_action('-scriptnames', {"-db" => @database.name}).body).each {|record|
|
66
66
|
name = record['SCRIPT_NAME']
|
67
67
|
self[name] = Rfm::Script.new(name, @database) if self[name] == nil
|
68
68
|
}
|
data/lib/rfm_result.rb
CHANGED
@@ -1,25 +1,36 @@
|
|
1
1
|
require 'bigdecimal'
|
2
|
+
require 'date'
|
2
3
|
|
3
4
|
module Rfm::Result
|
4
5
|
|
5
6
|
class ResultSet < Array
|
6
|
-
def initialize(fmresultset, layout = nil)
|
7
|
+
def initialize(server, fmresultset, layout = nil)
|
8
|
+
@server = server
|
7
9
|
@resultset = nil
|
8
10
|
@layout = layout
|
9
11
|
@fields = {}
|
10
12
|
@portals = {}
|
13
|
+
@date_format = nil
|
14
|
+
@time_format = nil
|
15
|
+
@timestamp_format = nil
|
11
16
|
|
12
17
|
doc = REXML::Document.new(fmresultset)
|
13
18
|
root = doc.root
|
14
19
|
|
15
20
|
# check for errors
|
16
21
|
error = root.elements['error'].attributes['code'].to_i
|
17
|
-
raise "Error #{error} occurred while processing the request" if error != 0 && error != 401
|
22
|
+
raise "Error #{error} occurred while processing the request" if error != 0 && (error != 401 || @server.state[:raise_on_401])
|
23
|
+
|
24
|
+
# ascertain date and time formats
|
25
|
+
datasource = root.elements['datasource']
|
26
|
+
@date_format = convertFormatString(datasource.attributes['date-format'])
|
27
|
+
@time_format = convertFormatString(datasource.attributes['time-format'])
|
28
|
+
@timestamp_format = convertFormatString(datasource.attributes['timestamp-format'])
|
18
29
|
|
19
30
|
# process field metadata
|
20
31
|
root.elements['metadata'].each_element('field-definition') { |field|
|
21
32
|
name = field.attributes['name']
|
22
|
-
@fields[name] = Field.new(field)
|
33
|
+
@fields[name] = Field.new(self, field)
|
23
34
|
}
|
24
35
|
@fields.freeze
|
25
36
|
|
@@ -29,7 +40,7 @@ module Rfm::Result
|
|
29
40
|
fields = {}
|
30
41
|
relatedset.each_element('field-definition') { |field|
|
31
42
|
name = field.attributes['name'].sub(Regexp.new(table + '::'), '')
|
32
|
-
fields[name] = Field.new(field)
|
43
|
+
fields[name] = Field.new(self, field)
|
33
44
|
}
|
34
45
|
@portals[table] = fields
|
35
46
|
}
|
@@ -41,7 +52,13 @@ module Rfm::Result
|
|
41
52
|
}
|
42
53
|
end
|
43
54
|
|
44
|
-
attr_reader :fields, :portals
|
55
|
+
attr_reader :server, :fields, :portals, :date_format, :time_format, :timestamp_format
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def convertFormatString(fm_format)
|
60
|
+
fm_format.gsub('MM', '%m').gsub('dd', '%d').gsub('yyyy', '%Y').gsub('HH', '%H').gsub('mm', '%M').gsub('ss', '%S')
|
61
|
+
end
|
45
62
|
|
46
63
|
end
|
47
64
|
|
@@ -90,7 +107,7 @@ module Rfm::Result
|
|
90
107
|
end
|
91
108
|
|
92
109
|
def save_if_not_modified
|
93
|
-
self.merge(@layout.edit(@record_id, @mods, {
|
110
|
+
self.merge(@layout.edit(@record_id, @mods, {:modification_id => @mod_id})[0]) if @mods.size > 0
|
94
111
|
@mods.clear
|
95
112
|
end
|
96
113
|
|
@@ -123,7 +140,8 @@ module Rfm::Result
|
|
123
140
|
end
|
124
141
|
|
125
142
|
class Field
|
126
|
-
def initialize(field)
|
143
|
+
def initialize(result_set, field)
|
144
|
+
@result_set = result_set
|
127
145
|
@name = field.attributes['name']
|
128
146
|
@result = field.attributes['result']
|
129
147
|
@type = field.attributes['type']
|
@@ -141,14 +159,13 @@ module Rfm::Result
|
|
141
159
|
when "number"
|
142
160
|
return BigDecimal.new(value)
|
143
161
|
when "date"
|
144
|
-
(
|
145
|
-
return Date.new(year.to_i, month.to_i, day.to_i)
|
162
|
+
return Date.strptime(value, @result_set.date_format)
|
146
163
|
when "time"
|
147
|
-
(
|
148
|
-
return DateTime.civil(-4712, 1, 1, hour.to_i, min.to_i, sec.to_i)
|
164
|
+
return DateTime.strptime("1/1/-4712 " + value, "%m/%d/%Y #{@result_set.time_format}")
|
149
165
|
when "timestamp"
|
150
|
-
(
|
151
|
-
|
166
|
+
return DateTime.strptime(value, @result_set.timestamp_format)
|
167
|
+
when "container"
|
168
|
+
return URI.parse("http://#{@result_set.server.host_name}:#{@result_set.server.port}#{value}")
|
152
169
|
end
|
153
170
|
end
|
154
171
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rfm
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date:
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-06-23 00:00:00 -07:00
|
8
8
|
summary: A package to access FileMaker Pro databases
|
9
9
|
require_paths:
|
10
10
|
- lib
|