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.
@@ -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