ginjo-rfm 3.0.9 → 3.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/CHANGELOG.md +61 -40
- data/README.md +8 -8
- data/lib/rfm.rb +66 -67
- data/lib/rfm/VERSION +1 -1
- data/lib/rfm/base.rb +237 -241
- data/lib/rfm/database.rb +38 -24
- data/lib/rfm/error.rb +25 -25
- data/lib/rfm/layout.rb +217 -195
- data/lib/rfm/metadata/datum.rb +37 -37
- data/lib/rfm/metadata/field.rb +42 -39
- data/lib/rfm/metadata/field_control.rb +72 -72
- data/lib/rfm/metadata/layout_meta.rb +32 -32
- data/lib/rfm/metadata/resultset_meta.rb +74 -74
- data/lib/rfm/metadata/script.rb +3 -3
- data/lib/rfm/metadata/value_list_item.rb +30 -30
- data/lib/rfm/record.rb +80 -77
- data/lib/rfm/resultset.rb +63 -65
- data/lib/rfm/server.rb +31 -23
- data/lib/rfm/utilities/case_insensitive_hash.rb +4 -1
- data/lib/rfm/utilities/compound_query.rb +100 -101
- data/lib/rfm/utilities/config.rb +228 -218
- data/lib/rfm/utilities/connection.rb +65 -57
- data/lib/rfm/utilities/core_ext.rb +114 -119
- data/lib/rfm/utilities/factory.rb +122 -126
- data/lib/rfm/utilities/sax_parser.rb +1058 -1046
- data/lib/rfm/utilities/scope.rb +64 -0
- data/lib/rfm/version.rb +23 -7
- metadata +40 -39
data/lib/rfm/metadata/script.rb
CHANGED
@@ -1,31 +1,31 @@
|
|
1
1
|
module Rfm
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end # Rfm
|
2
|
+
module Metadata
|
3
|
+
|
4
|
+
# The ValueListItem object represents an item in a Filemaker value list.
|
5
|
+
# ValueListItem is subclassed from String, so you can use it just like
|
6
|
+
# a string. It does have three additional methods to help separate Filemaker *value*
|
7
|
+
# vs *display* items.
|
8
|
+
#
|
9
|
+
# Getting values vs display items:
|
10
|
+
#
|
11
|
+
# * *#value* the value list item value
|
12
|
+
#
|
13
|
+
# * *#display* is the value list item display. It could be the same
|
14
|
+
# as +value+, or it could be the "second field", if that option is checked in Filemaker
|
15
|
+
#
|
16
|
+
# * *#value_list_name* is the name of the parent value list, if any
|
17
|
+
class ValueListItem < String
|
18
|
+
# TODO: re-instate saving of value_list_name.
|
19
|
+
attr_reader :value, :display, :value_list_name
|
20
|
+
|
21
|
+
# def initialize(value, display, value_list_name)
|
22
|
+
# @value_list_name = value_list_name
|
23
|
+
# @value = value.to_s
|
24
|
+
# @display = display.to_s
|
25
|
+
# self.replace @value
|
26
|
+
# end
|
27
|
+
|
28
|
+
end # ValueListItem
|
29
|
+
|
30
|
+
end # Metadata
|
31
|
+
end # Rfm
|
data/lib/rfm/record.rb
CHANGED
@@ -71,13 +71,13 @@ module Rfm
|
|
71
71
|
#
|
72
72
|
# This code iterates through the rows of the _Orders_ portal.
|
73
73
|
#
|
74
|
-
#
|
74
|
+
# As a convenience, you can call a specific portal as a method on your record, if the table occurrence name does
|
75
75
|
# not have any characters that are prohibited in ruby method names, just as you can call a field with a method:
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
76
|
+
#
|
77
|
+
# myRecord.orders.each {|portal_row|
|
78
|
+
# puts portal_row["Order Number"]
|
79
|
+
# }
|
80
|
+
#
|
81
81
|
# =Field Types and Ruby Types
|
82
82
|
#
|
83
83
|
# RFM automatically converts data from FileMaker into a Ruby object with the most reasonable type possible. The
|
@@ -107,41 +107,40 @@ module Rfm
|
|
107
107
|
# changes so you can tell if the Record object you're looking at is up-to-date as compared to another
|
108
108
|
# copy of the same record
|
109
109
|
class Record < Rfm::CaseInsensitiveHash
|
110
|
-
|
110
|
+
|
111
111
|
attr_accessor :layout #, :resultset
|
112
112
|
attr_reader :record_id, :mod_id, :portals
|
113
113
|
def_delegators :layout, :db, :database, :server, :field_meta, :portal_meta, :field_names, :portal_names
|
114
|
-
|
114
|
+
|
115
115
|
# This is called during the parsing process, but only to allow creation of the correct type of model instance.
|
116
116
|
# This is also called by the end-user when constructing a new empty record, but it is called from the model subclass.
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
117
|
+
def self.new(*args) # resultset
|
118
|
+
record = case
|
119
|
+
# Get model from layout, then allocate record.
|
120
|
+
# This should only use model class if the class already exists,
|
121
|
+
# since we don't want to create classes that aren't defined by the user - they won't be persistant.
|
122
|
+
when args[0].is_a?(Resultset) && args[0].layout && args[0].layout.model
|
123
|
+
args[0].layout.modelize.allocate
|
124
|
+
# Allocate instance of Rfm::Record.
|
125
|
+
else
|
126
|
+
self.allocate
|
127
|
+
end
|
125
128
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
# rescue
|
133
|
-
# puts "Record.new bombed and is defaulting to super.new. Error: #{$!}"
|
134
|
-
# super
|
135
|
-
end
|
129
|
+
record.send(:initialize, *args)
|
130
|
+
record
|
131
|
+
# rescue
|
132
|
+
# puts "Record.new bombed and is defaulting to super.new. Error: #{$!}"
|
133
|
+
# super
|
134
|
+
end
|
136
135
|
|
137
136
|
def initialize(*args) # resultset, attributes
|
138
|
-
@mods
|
137
|
+
@mods ||= {}
|
139
138
|
@portals ||= Rfm::CaseInsensitiveHash.new
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
139
|
+
options = args.rfm_extract_options!
|
140
|
+
if args[0].is_a?(Resultset)
|
141
|
+
@layout = args[0].layout
|
142
|
+
elsif self.is_a?(Base)
|
143
|
+
@layout = self.class.layout
|
145
144
|
@layout.field_keys.each do |field|
|
146
145
|
self[field] = nil
|
147
146
|
end
|
@@ -150,8 +149,8 @@ module Rfm
|
|
150
149
|
@loaded = true
|
151
150
|
end
|
152
151
|
_attach_as_instance_variables(args[1]) if args[1].is_a? Hash
|
153
|
-
|
154
|
-
|
152
|
+
#@loaded = true
|
153
|
+
self
|
155
154
|
end
|
156
155
|
|
157
156
|
# Saves local changes to the Record object back to Filemaker. For example:
|
@@ -181,7 +180,7 @@ module Rfm
|
|
181
180
|
self.replace_with_fresh_data(layout.edit(@record_id, @mods, {:modification_id => @mod_id})[0]) if @mods.size > 0
|
182
181
|
@mods.clear
|
183
182
|
end
|
184
|
-
|
183
|
+
|
185
184
|
# Gets the value of a field from the record. For example:
|
186
185
|
#
|
187
186
|
# first = myRecord["First Name"]
|
@@ -195,19 +194,19 @@ module Rfm
|
|
195
194
|
#
|
196
195
|
# When you do, the change is noted, but *the data is not updated in FileMaker*. You must call
|
197
196
|
# Record::save or Record::save_if_not_modified to actually save the data.
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
197
|
+
def [](key)
|
198
|
+
# Added by wbr, 2013-03-31
|
199
|
+
return super unless @loaded
|
200
|
+
return fetch(key.to_s.downcase)
|
201
|
+
rescue IndexError
|
202
|
+
raise Rfm::ParameterError, "#{key} does not exists as a field in the current Filemaker layout." unless key.to_s == '' #unless (!layout or self.key?(key_string))
|
203
|
+
end
|
205
204
|
|
206
205
|
def respond_to?(symbol, include_private = false)
|
207
206
|
return true if self.include?(symbol.to_s)
|
208
207
|
super
|
209
208
|
end
|
210
|
-
|
209
|
+
|
211
210
|
def []=(key, val)
|
212
211
|
key_string = key.to_s.downcase
|
213
212
|
return super unless @loaded # is this needed?
|
@@ -215,45 +214,49 @@ module Rfm
|
|
215
214
|
# @mods[key_string] = val
|
216
215
|
# TODO: This needs cleaning up.
|
217
216
|
# TODO: can we get field_type from record instead?
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
217
|
+
@mods[key_string] = if [Date, Time, DateTime].member?(val.class)
|
218
|
+
field_type = layout.field_meta[key_string.to_sym].result
|
219
|
+
case field_type
|
220
|
+
when 'time'
|
221
|
+
val.strftime(layout.time_format)
|
222
|
+
when 'date'
|
223
|
+
val.strftime(layout.date_format)
|
224
|
+
when 'timestamp'
|
225
|
+
val.strftime(layout.timestamp_format)
|
226
|
+
else
|
227
|
+
val
|
228
|
+
end
|
229
|
+
else
|
230
|
+
val
|
231
|
+
end
|
228
232
|
super(key, val)
|
229
233
|
end
|
230
|
-
|
231
|
-
|
232
|
-
|
234
|
+
|
235
|
+
def field_names
|
236
|
+
layout.field_names
|
233
237
|
end
|
234
|
-
|
238
|
+
|
235
239
|
def replace_with_fresh_data(record)
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
240
|
+
self.replace record
|
241
|
+
[:@mod_id, :@record_id, :@portals, :@mods].each do |var|
|
242
|
+
self.instance_variable_set var, record.instance_variable_get(var) || {}
|
243
|
+
end
|
244
|
+
self
|
245
|
+
end
|
246
|
+
|
244
247
|
|
245
|
-
|
248
|
+
private
|
246
249
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
250
|
+
def method_missing (symbol, *attrs, &block)
|
251
|
+
method = symbol.to_s
|
252
|
+
return self[method] if self.key?(method)
|
253
|
+
return @portals[method] if @portals and @portals.key?(method)
|
254
|
+
|
255
|
+
if method =~ /(=)$/
|
256
|
+
return self[$`] = attrs.first if self.key?($`)
|
257
|
+
end
|
258
|
+
super
|
259
|
+
end
|
251
260
|
|
252
|
-
if method =~ /(=)$/
|
253
|
-
return self[$`] = attrs.first if self.key?($`)
|
254
|
-
end
|
255
|
-
super
|
256
|
-
end
|
257
|
-
|
258
261
|
end # Record
|
259
|
-
end # Rfm
|
262
|
+
end # Rfm
|
data/lib/rfm/resultset.rb
CHANGED
@@ -36,25 +36,23 @@ module Rfm
|
|
36
36
|
# it provides metadata about the portals in the ResultSet and the Fields on those portals
|
37
37
|
|
38
38
|
class Resultset < Array
|
39
|
-
|
40
|
-
|
39
|
+
include Config
|
40
|
+
|
41
41
|
attr_reader :layout, :meta, :calling_object
|
42
|
-
# attr_reader :layout, :database, :server, :calling_object, :doc
|
43
|
-
# attr_reader :field_meta, :portal_meta, :include_portals, :datasource
|
44
|
-
# attr_reader :date_format, :time_format, :timestamp_format
|
45
|
-
# attr_reader :total_count, :foundset_count, :table
|
42
|
+
# attr_reader :layout, :database, :server, :calling_object, :doc
|
43
|
+
# attr_reader :field_meta, :portal_meta, :include_portals, :datasource
|
44
|
+
# attr_reader :date_format, :time_format, :timestamp_format
|
45
|
+
# attr_reader :total_count, :foundset_count, :table
|
46
46
|
#def_delegators :layout, :db, :database
|
47
47
|
# alias_method :db, :database
|
48
|
-
|
48
|
+
def_delegators :meta, :field_meta, :portal_meta, :date_format, :time_format, :timestamp_format, :total_count, :foundset_count, :fetch_size, :table, :error, :field_names, :field_keys, :portal_names
|
49
49
|
|
50
|
-
|
51
50
|
class << self
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
51
|
+
def load_data(data, object=self.new)
|
52
|
+
Rfm::SaxParser.parse(data, :fmresultset, object)
|
53
|
+
end
|
56
54
|
end
|
57
|
-
|
55
|
+
|
58
56
|
# Initializes a new ResultSet object. You will probably never do this your self (instead, use the Layout
|
59
57
|
# object to get various ResultSet obejects).
|
60
58
|
#
|
@@ -77,68 +75,68 @@ module Rfm
|
|
77
75
|
# layout contains portals, you can find out what fields they contain here. Again, if it's the data you're
|
78
76
|
# after, you want to look at the Record object.
|
79
77
|
def initialize(*args) # parent, layout
|
80
|
-
|
81
|
-
|
78
|
+
config(*args)
|
79
|
+
self.meta
|
82
80
|
end # initialize
|
83
81
|
|
84
82
|
def config(*args)
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
83
|
+
super do |params|
|
84
|
+
(@layout = params[:objects][0]) if params &&
|
85
|
+
params[:objects] &&
|
86
|
+
params[:objects][0] &&
|
87
|
+
params[:objects][0].is_a?(Rfm::Layout)
|
88
|
+
end
|
91
89
|
end
|
92
|
-
|
90
|
+
|
93
91
|
# This method was added for situations where a layout was not provided at resultset instantiation,
|
94
92
|
# such as when loading a resultset from an xml file.
|
95
93
|
def layout
|
96
|
-
|
94
|
+
@layout ||= (Layout.new(meta.layout, self) if meta.respond_to? :layout)
|
95
|
+
end
|
96
|
+
|
97
|
+
def database
|
98
|
+
layout.database
|
99
|
+
end
|
100
|
+
|
101
|
+
alias_method :db, :database
|
102
|
+
|
103
|
+
def server
|
104
|
+
database.server
|
105
|
+
end
|
106
|
+
|
107
|
+
def meta
|
108
|
+
# Access the meta inst var here.
|
109
|
+
@meta ||= Metadata::ResultsetMeta.new
|
110
|
+
end
|
111
|
+
|
112
|
+
# Deprecated on 7/29/2014. Stop using.
|
113
|
+
def handle_new_record(attributes)
|
114
|
+
r = Rfm::Record.new(self, attributes, {})
|
115
|
+
self << r
|
116
|
+
r
|
117
|
+
end
|
118
|
+
|
119
|
+
def end_datasource_element_callback(cursor)
|
120
|
+
%w(date_format time_format timestamp_format).each{|f| convert_date_time_format(send(f))}
|
121
|
+
@meta.attach_layout_object_from_cursor(cursor)
|
97
122
|
end
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
alias_method :db, :database
|
104
|
-
|
105
|
-
def server
|
106
|
-
database.server
|
107
|
-
end
|
108
|
-
|
109
|
-
def meta
|
110
|
-
# Access the meta inst var here.
|
111
|
-
@meta ||= Metadata::ResultsetMeta.new
|
112
|
-
end
|
113
|
-
|
114
|
-
# Deprecated on 7/29/2014. Stop using.
|
115
|
-
def handle_new_record(attributes)
|
116
|
-
r = Rfm::Record.new(self, attributes, {})
|
117
|
-
self << r
|
118
|
-
r
|
119
|
-
end
|
120
|
-
|
121
|
-
def end_datasource_element_callback(cursor)
|
122
|
-
%w(date_format time_format timestamp_format).each{|f| convert_date_time_format(send(f))}
|
123
|
-
@meta.attach_layout_object_from_cursor(cursor)
|
124
|
-
end
|
125
|
-
|
126
|
-
private
|
127
|
-
|
123
|
+
|
124
|
+
private
|
125
|
+
|
128
126
|
def check_for_errors(error_code=@meta['error'].to_i, raise_401=state[:raise_401])
|
129
|
-
|
127
|
+
#puts ["\nRESULTSET#check_for_errors", "meta[:error] #{@meta[:error]}", "error_code: #{error_code}", "raise_401: #{raise_401}"]
|
130
128
|
raise Rfm::Error.getError(error_code) if error_code != 0 && (error_code != 401 || raise_401)
|
131
129
|
end
|
132
130
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
131
|
+
def convert_date_time_format(fm_format)
|
132
|
+
fm_format.gsub!('MM', '%m')
|
133
|
+
fm_format.gsub!('dd', '%d')
|
134
|
+
fm_format.gsub!('yyyy', '%Y')
|
135
|
+
fm_format.gsub!('HH', '%H')
|
136
|
+
fm_format.gsub!('mm', '%M')
|
137
|
+
fm_format.gsub!('ss', '%S')
|
138
|
+
fm_format
|
139
|
+
end
|
140
|
+
|
143
141
|
end
|
144
|
-
end
|
142
|
+
end
|