ginjo-rfm 3.0.9 → 3.0.10
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.
- 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
|