ginjo-rfm 1.4.4 → 2.0.pre31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,17 +65,21 @@ module Rfm
65
65
  # myDatabase = myServer["Customers"]
66
66
  #
67
67
  # This sample code gets a database object representing the Customers database on the FileMaker server.
68
- def initialize(name, server)
69
- @name = name
70
- @server = server
71
- @account_name = server.state[:account_name] or ""
72
- @password = server.state[:password] or ""
68
+ def initialize(name, server_obj, acnt=nil, pass=nil)
69
+ raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Database has no name.") if name.to_s == ''
70
+ @name = name.to_s
71
+ rfm_metaclass.instance_variable_set :@server, server_obj
72
+ @account_name = acnt #server.state[:account_name] or ""
73
+ @password = pass #server.state[:password] or ""
73
74
  @layout = Rfm::Factory::LayoutFactory.new(server, self)
74
75
  @script = Rfm::Factory::ScriptFactory.new(server, self)
75
76
  end
76
77
 
77
- attr_reader :server, :name, :account_name, :password, :layout, :script
78
+ meta_attr_reader :server
79
+ #attr_reader :server
80
+ attr_reader :name, :account_name, :password, :layout, :script
78
81
  attr_writer :account_name, :password
82
+ alias_method :layouts, :layout
79
83
 
80
84
  # Access the Layout object representing a layout in this database. For example:
81
85
  #
@@ -88,9 +92,10 @@ module Rfm
88
92
  # returned is created on the fly and assumed to refer to a valid layout, but you will
89
93
  # get no error at this point if the layout you specify doesn't exist. Instead, you'll
90
94
  # receive an error when you actually try to perform some action it.
91
- def [](layout_name)
92
- self.layout[layout_name]
93
- end
95
+ # def [](layout_name)
96
+ # self.layout[layout_name]
97
+ # end
98
+ def_delegator :layout, :[]
94
99
 
95
100
  end
96
101
  end
@@ -119,6 +119,8 @@ module Rfm
119
119
  # list that is attached to any field on the layout
120
120
 
121
121
  class Layout
122
+
123
+ include ComplexQuery
122
124
 
123
125
  # Initialize a layout object. You never really need to do this. Instead, just do this:
124
126
  #
@@ -133,127 +135,182 @@ module Rfm
133
135
  #
134
136
  # myServer = Rfm::Server.new(...)
135
137
  # myLayout = myServer["Customers"]["Details"]
136
- def initialize(name, db)
137
- @name = name
138
- @db = db
138
+ def initialize(name, db_obj)
139
+ raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Layout has no name.") if name.to_s == ''
140
+ @name = name.to_s
141
+ rfm_metaclass.instance_variable_set :@db, db_obj
139
142
 
140
143
  @loaded = false
141
144
  @field_controls = Rfm::CaseInsensitiveHash.new
142
- @value_lists = Rfm::CaseInsensitiveHash.new
145
+ @value_lists = Rfm::CaseInsensitiveHash.new
146
+ # @portal_meta = nil
147
+ # @field_names = nil
143
148
  end
144
149
 
145
- attr_reader :name, :db
150
+ meta_attr_reader :db
151
+ attr_reader :name #, :db
152
+ attr_writer :field_names, :portal_meta, :table
153
+ def_delegator :db, :server
154
+ alias_method :database, :db
146
155
 
147
- # Returns a ResultSet object containing _every record_ in the table associated with this layout.
148
- def all(options = {})
149
- get_records('-findall', {}, options)
150
- end
151
156
 
152
- # Returns a ResultSet containing a single random record from the table associated with this layout.
153
- def any(options = {})
154
- get_records('-findany', {}, options)
155
- end
156
-
157
- # Finds a record. Typically you will pass in a hash of field names and values. For example:
158
- #
159
- # myLayout.find({"First Name" => "Bill"})
160
- #
161
- # Values in the hash work just like value in FileMaker's Find mode. You can use any special
162
- # symbols (+==+, +...+, +>+, etc...).
163
- #
164
- # If you pass anything other than a hash as the first parameter, it is converted to a string and
165
- # assumed to be FileMaker's internal id for a record (the recid).
166
- def find(hash_or_recid, options = {})
167
- if hash_or_recid.kind_of? Hash
168
- get_records('-find', hash_or_recid, options)
169
- else
170
- get_records('-find', {'-recid' => hash_or_recid.to_s}, options)
171
- end
172
- end
173
-
174
- # Updates the contents of the record whose internal +recid+ is specified. Send in a hash of new
175
- # data in the +values+ parameter. Returns a RecordSet containing the modified record. For example:
176
- #
177
- # recid = myLayout.find({"First Name" => "Bill"})[0].record_id
178
- # myLayout.edit(recid, {"First Name" => "Steve"})
179
- #
180
- # The above code would find the first record with _Bill_ in the First Name field and change the
181
- # first name to _Steve_.
182
- def edit(recid, values, options = {})
183
- get_records('-edit', {'-recid' => recid}.merge(values), options)
184
- end
157
+ # These methods are to be inclulded in Layout and SubLayout, so that
158
+ # they have their own descrete 'self' in the master class and the subclass.
159
+ module LayoutModule
185
160
 
186
- # Creates a new record in the table associated with this layout. Pass field data as a hash in the
187
- # +values+ parameter. Returns the newly created record in a RecordSet. You can use the returned
188
- # record to, ie, discover the values in auto-enter fields (like serial numbers).
189
- #
190
- # For example:
191
- #
192
- # result = myLayout.create({"First Name" => "Jerry", "Last Name" => "Robin"})
193
- # id = result[0]["ID"]
194
- #
195
- # The above code adds a new record with first name _Jerry_ and last name _Robin_. It then
196
- # puts the value from the ID field (a serial number) into a ruby variable called +id+.
197
- def create(values, options = {})
198
- get_records('-new', values, options)
199
- end
161
+ # Returns a ResultSet object containing _every record_ in the table associated with this layout.
162
+ def all(options = {})
163
+ get_records('-findall', {}, options)
164
+ end
165
+
166
+ # Returns a ResultSet containing a single random record from the table associated with this layout.
167
+ def any(options = {})
168
+ get_records('-findany', {}, options)
169
+ end
170
+
171
+ # Finds a record. Typically you will pass in a hash of field names and values. For example:
172
+ #
173
+ # myLayout.find({"First Name" => "Bill"})
174
+ #
175
+ # Values in the hash work just like value in FileMaker's Find mode. You can use any special
176
+ # symbols (+==+, +...+, +>+, etc...).
177
+ #
178
+ # If you pass anything other than a hash as the first parameter, it is converted to a string and
179
+ # assumed to be FileMaker's internal id for a record (the recid).
180
+ def find(hash_or_recid, options = {})
181
+ if hash_or_recid.kind_of? Hash
182
+ get_records('-find', hash_or_recid, options)
183
+ else
184
+ get_records('-find', {'-recid' => hash_or_recid.to_s}, options)
185
+ end
186
+ end
187
+
188
+ # Updates the contents of the record whose internal +recid+ is specified. Send in a hash of new
189
+ # data in the +values+ parameter. Returns a RecordSet containing the modified record. For example:
190
+ #
191
+ # recid = myLayout.find({"First Name" => "Bill"})[0].record_id
192
+ # myLayout.edit(recid, {"First Name" => "Steve"})
193
+ #
194
+ # The above code would find the first record with _Bill_ in the First Name field and change the
195
+ # first name to _Steve_.
196
+ def edit(recid, values, options = {})
197
+ get_records('-edit', {'-recid' => recid}.merge(values), options)
198
+ end
199
+
200
+ # Creates a new record in the table associated with this layout. Pass field data as a hash in the
201
+ # +values+ parameter. Returns the newly created record in a RecordSet. You can use the returned
202
+ # record to, ie, discover the values in auto-enter fields (like serial numbers).
203
+ #
204
+ # For example:
205
+ #
206
+ # result = myLayout.create({"First Name" => "Jerry", "Last Name" => "Robin"})
207
+ # id = result[0]["ID"]
208
+ #
209
+ # The above code adds a new record with first name _Jerry_ and last name _Robin_. It then
210
+ # puts the value from the ID field (a serial number) into a ruby variable called +id+.
211
+ def create(values, options = {})
212
+ get_records('-new', values, options)
213
+ end
214
+
215
+ # Deletes the record with the specified internal recid. Returns a ResultSet with the deleted record.
216
+ #
217
+ # For example:
218
+ #
219
+ # recid = myLayout.find({"First Name" => "Bill"})[0].record_id
220
+ # myLayout.delete(recid)
221
+ #
222
+ # The above code finds every record with _Bill_ in the First Name field, then deletes the first one.
223
+ def delete(recid, options = {})
224
+ get_records('-delete', {'-recid' => recid}, options)
225
+ return nil
226
+ end
227
+
228
+ def get_records(action, extra_params = {}, options = {})
229
+ include_portals = options[:include_portals] ? options.delete(:include_portals) : nil
230
+ xml_response = db.server.connect(db.account_name, db.password, action, params.merge(extra_params), options).body
231
+ Rfm::Resultset.new(db.server, xml_response, self, include_portals)
232
+ end
233
+
234
+ def params
235
+ {"-db" => db.name, "-lay" => self.name}
236
+ end
237
+
238
+ end # LayoutModule
239
+ include LayoutModule
200
240
 
201
- # Deletes the record with the specified internal recid. Returns a ResultSet with the deleted record.
202
- #
203
- # For example:
204
- #
205
- # recid = myLayout.find({"First Name" => "Bill"})[0].record_id
206
- # myLayout.delete(recid)
207
- #
208
- # The above code finds every record with _Bill_ in the First Name field, then deletes the first one.
209
- def delete(recid, options = {})
210
- get_records('-delete', {'-recid' => recid}, options)
211
- return nil
212
- end
213
241
 
214
242
  def field_controls
215
243
  load unless @loaded
216
244
  @field_controls
217
245
  end
218
246
 
247
+ def field_names
248
+ load unless @field_names
249
+ @field_names
250
+ end
251
+
252
+ def field_names_no_load
253
+ @field_names
254
+ end
255
+
219
256
  def value_lists
220
257
  load unless @loaded
221
258
  @value_lists
222
259
  end
223
260
 
224
- private
261
+ def total_count
262
+ any.total_count
263
+ end
264
+
265
+ def portal_meta
266
+ @portal_meta ||= any.portal_meta
267
+ end
268
+
269
+ def portal_meta_no_load
270
+ @portal_meta
271
+ end
272
+
273
+ def portal_names
274
+ portal_meta.keys
275
+ end
276
+
277
+ def table
278
+ @table ||= any.table
279
+ end
280
+
281
+ def table_no_load
282
+ @table
283
+ end
225
284
 
226
285
  def load
227
- #require 'rexml/document'
228
- require 'nokogiri'
229
-
230
286
  @loaded = true
231
- fmpxmllayout = @db.server.load_layout(self)
232
- doc = Nokogiri::XML(fmpxmllayout)
287
+ fmpxmllayout = db.server.load_layout(self)
288
+ doc = XmlParser.new(fmpxmllayout.body, :namespace=>false, :parser=>server.state[:parser])
233
289
 
234
290
  # check for errors
235
- error = doc.xpath('//ERRORCODE').children[0].to_s.to_i
291
+ error = doc['FMPXMLLAYOUT']['ERRORCODE'].to_s.to_i
236
292
  raise Rfm::Error::FileMakerError.getError(error) if error != 0
237
293
 
238
294
  # process valuelists
239
- if doc.xpath('//VALUELIST').size > 0
240
- doc.xpath('//VALUELIST').each {|valuelist|
295
+ vlists = doc['FMPXMLLAYOUT']['VALUELISTS']['VALUELIST']
296
+ if !vlists.nil? #root.elements['VALUELISTS'].size > 0
297
+ vlists.each {|valuelist|
241
298
  name = valuelist['NAME']
242
- @value_lists[name] = valuelist.children.collect{|value|
243
- Rfm::Metadata::ValueListItem.new(value.children[0].to_s, value['DISPLAY'], name)
299
+ @value_lists[name] = valuelist['VALUE'].collect{|value|
300
+ Rfm::Metadata::ValueListItem.new(value['__content__'], value['DISPLAY'], name)
244
301
  } rescue []
245
302
  }
246
303
  @value_lists.freeze
247
304
  end
248
305
 
249
306
  # process field controls
250
- doc.xpath('//FIELD').each {|field|
307
+ doc['FMPXMLLAYOUT']['LAYOUT']['FIELD'].each {|field|
251
308
  name = field['NAME']
252
- style_xml = field.children[0]
253
- style = style_xml['TYPE']
254
- value_list_name = style_xml['VALUELIST']
309
+ style = field['STYLE']
310
+ type = style['TYPE']
311
+ value_list_name = style['VALUELIST']
255
312
  value_list = @value_lists[value_list_name] if value_list_name != ''
256
- field_control = Rfm::Metadata::FieldControl.new(name, style, value_list_name, value_list)
313
+ field_control = Rfm::Metadata::FieldControl.new(name, type, value_list_name, value_list)
257
314
  existing = @field_controls[name]
258
315
  if existing
259
316
  if existing.kind_of?(Array)
@@ -265,17 +322,12 @@ module Rfm
265
322
  @field_controls[name] = field_control
266
323
  end
267
324
  }
268
- @field_controls.freeze
269
- end
270
-
271
- def get_records(action, extra_params = {}, options = {})
272
- include_portals = options[:include_portals] ? options.delete(:include_portals) : nil
273
- xml_response = @db.server.connect(@db.account_name, @db.password, action, params.merge(extra_params), options).body
274
- Rfm::Resultset.new(@db.server, xml_response, self, include_portals)
325
+ @field_names ||= @field_controls.collect{|k,v| v.name rescue v[0].name}
326
+ @field_controls.freeze
275
327
  end
276
328
 
277
- def params
278
- {"-db" => @db.name, "-lay" => self.name}
279
- end
280
- end
281
- end
329
+ private :load, :get_records, :params
330
+
331
+
332
+ end # Layout
333
+ end # Rfm
@@ -1,5 +1,6 @@
1
1
  module Rfm
2
2
  module Metadata
3
+
3
4
  # The Field object represents a single FileMaker field. It *does not hold the data* in the field. Instead,
4
5
  # it serves as a source of metadata about the field. For example, if you're script is trying to be highly
5
6
  # dynamic about its field access, it may need to determine the data type of a field at run time. Here's
@@ -56,7 +57,6 @@ module Rfm
56
57
  #
57
58
  # The code above makes sure the control is always an array. Typically, though, you'll know up front
58
59
  # if the control is an array or not, and you can code accordingly.
59
-
60
60
  class Field
61
61
 
62
62
  attr_reader :name, :result, :type, :max_repeats, :global
@@ -75,7 +75,7 @@ module Rfm
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)
78
- return nil if value.empty?
78
+ return nil if (value.nil? or value.empty?)
79
79
  case self.result
80
80
  when "text" then value
81
81
  when "number" then BigDecimal.new(value)
@@ -88,6 +88,6 @@ module Rfm
88
88
 
89
89
  end
90
90
 
91
- end
92
- end
93
- end
91
+ end # Field
92
+ end # Metadata
93
+ end # Rfm
@@ -1,53 +1,54 @@
1
1
  module Rfm
2
- module Metadata
3
-
4
- # The FieldControl object represents a field on a FileMaker layout. You can find out what field
5
- # style the field uses, and the value list attached to it.
6
- #
7
- # =Attributes
8
- #
9
- # * *name* is the name of the field
10
- #
11
- # * *style* is any one of:
12
- # * * :edit_box - a normal editable field
13
- # * * :scrollable - an editable field with scroll bar
14
- # * * :popup_menu - a pop-up menu
15
- # * * :checkbox_set - a set of checkboxes
16
- # * * :radio_button_set - a set of radio buttons
17
- # * * :popup_list - a pop-up list
18
- # * * :calendar - a pop-up calendar
19
- #
20
- # * *value_list_name* is the name of the attached value list, if any
21
- #
22
- # * *value_list* is an array of strings representing the value list items, or nil
23
- # if this field has no attached value list
24
- class FieldControl
25
- def initialize(name, style, value_list_name, value_list)
26
- @name = name
27
- case style
28
- when "EDITTEXT"
29
- @style = :edit_box
30
- when "POPUPMENU"
31
- @style = :popup_menu
32
- when "CHECKBOX"
33
- @style = :checkbox_set
34
- when "RADIOBUTTONS"
35
- @style = :radio_button_set
36
- when "POPUPLIST"
37
- @style = :popup_list
38
- when "CALENDAR"
39
- @style = :calendar
40
- when "SCROLLTEXT"
41
- @style = :scrollable
42
- else
43
- nil
44
- end
45
- @value_list_name = value_list_name
46
- @value_list = value_list
47
- end
48
-
49
- attr_reader :name, :style, :value_list_name, :value_list
50
-
51
- end
52
- end
2
+ module Metadata
3
+
4
+ # The FieldControl object represents a field on a FileMaker layout. You can find out what field
5
+ # style the field uses, and the value list attached to it.
6
+ #
7
+ # =Attributes
8
+ #
9
+ # * *name* is the name of the field
10
+ #
11
+ # * *style* is any one of:
12
+ # * * :edit_box - a normal editable field
13
+ # * * :scrollable - an editable field with scroll bar
14
+ # * * :popup_menu - a pop-up menu
15
+ # * * :checkbox_set - a set of checkboxes
16
+ # * * :radio_button_set - a set of radio buttons
17
+ # * * :popup_list - a pop-up list
18
+ # * * :calendar - a pop-up calendar
19
+ #
20
+ # * *value_list_name* is the name of the attached value list, if any
21
+ #
22
+ # * *value_list* is an array of strings representing the value list items, or nil
23
+ # if this field has no attached value list
24
+ class FieldControl
25
+ def initialize(name, style, value_list_name, value_list)
26
+ @name = name
27
+ case style
28
+ when "EDITTEXT"
29
+ @style = :edit_box
30
+ when "POPUPMENU"
31
+ @style = :popup_menu
32
+ when "CHECKBOX"
33
+ @style = :checkbox_set
34
+ when "RADIOBUTTONS"
35
+ @style = :radio_button_set
36
+ when "POPUPLIST"
37
+ @style = :popup_list
38
+ when "CALENDAR"
39
+ @style = :calendar
40
+ when "SCROLLTEXT"
41
+ @style = :scrollable
42
+ else
43
+ nil
44
+ end
45
+ @value_list_name = value_list_name
46
+ rfm_metaclass.instance_variable_set :@value_list, value_list
47
+ end
48
+
49
+ attr_reader :name, :style, :value_list_name
50
+ meta_attr_reader :value_list
51
+
52
+ end
53
+ end
53
54
  end