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 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
- Code Samples
4
- ============
3
+ Installation
4
+ ------------
5
5
 
6
- To access the API you need to require the right pieces. The easiest way to do this is to:
6
+ rfm is availabl as a gem from rubyforge.org, so you can get it like this:
7
7
 
8
- require 'rfm/rfm'
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
- near the top of your script. This will cause everhting in the API to be loaded.
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. This is open for debate...
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
- }.merge(options)
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
- puts result
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 :pre_find_scripts:
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 = false
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, {'-modid' => @mod_id})[0]) if @mods.size > 0
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
- (day, month, year) = /(\d\d)\/(\d\d)\/(\d+)/.match(value)
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
- (hour, min, sec) = /(\d+):(\d+):(\d+)/.match(value)
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
- (month, day, year, hour, min, sec) = /(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+):(\d+)/.match(value)
151
- return DateTime.civil(year.to_i, month.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i)
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.1.0
7
- date: 2006-09-28 00:00:00 -07:00
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