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/datum.rb
CHANGED
@@ -1,48 +1,48 @@
|
|
1
1
|
#require 'delegate'
|
2
2
|
module Rfm
|
3
3
|
module Metadata
|
4
|
-
|
4
|
+
|
5
5
|
class Datum #< DelegateClass(Field)
|
6
|
-
|
6
|
+
|
7
7
|
def get_mapped_name(name, resultset)
|
8
|
-
|
9
|
-
|
8
|
+
#puts ["\nDATUM#get_mapped_name", "name: #{name}", "mapping: #{resultset.layout.field_mapping.to_yaml}"]
|
9
|
+
(resultset && resultset.layout && resultset.layout.field_mapping[name]) || name
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
# NOT sure what this method is for. Can't find a reference to it.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
13
|
+
def main_callback(cursor)
|
14
|
+
resultset = cursor.top.object
|
15
|
+
name = get_mapped_name(@attributes['name'].to_s, resultset)
|
16
|
+
field = resultset.field_meta[name]
|
17
|
+
data = @attributes['data']
|
18
|
+
cursor.parent.object[name.downcase] = field.coerce(data)
|
19
|
+
end
|
20
|
+
|
21
|
+
def portal_field_element_close_callback(cursor)
|
22
|
+
resultset = cursor.top.object
|
23
|
+
table, name = @attributes['name'].to_s.split('::')
|
24
|
+
#puts ['DATUM_portal_field_element_close_callback_01', table, name].join(', ')
|
25
|
+
name = get_mapped_name(name, resultset)
|
26
|
+
field = resultset.portal_meta[table.downcase][name.downcase]
|
27
|
+
data = @attributes['data']
|
28
|
+
#puts ['DATUM_portal_field_element_close_callback_02', "cursor.parent.object.class: #{cursor.parent.object.class}", "resultset.class: #{resultset.class}", "table: #{table}", "name: #{name}", "field: #{field}", "data: #{data}"]
|
29
|
+
#(puts resultset.portal_meta.to_yaml) unless field
|
30
|
+
cursor.parent.object[name.downcase] = field.coerce(data)
|
31
|
+
end
|
32
|
+
|
33
33
|
# Should return value only.
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
def field_element_close_callback(cursor)
|
35
|
+
record = cursor.parent.object
|
36
|
+
resultset = cursor.top.object
|
37
|
+
|
38
|
+
name = get_mapped_name(@attributes['name'].to_s, resultset)
|
39
|
+
field = resultset.field_meta[name]
|
40
|
+
data = @attributes['data'] #'data'
|
41
|
+
#puts ["\nDATUM", name, record.class, resultset.class, data]
|
42
|
+
#puts ["\nDATUM", self.to_yaml]
|
43
|
+
record[name] = field.coerce(data)
|
44
|
+
end
|
37
45
|
|
38
|
-
name = get_mapped_name(@attributes['name'].to_s, resultset)
|
39
|
-
field = resultset.field_meta[name]
|
40
|
-
data = @attributes['data'] #'data'
|
41
|
-
#puts ["\nDATUM", name, record.class, resultset.class, data]
|
42
|
-
#puts ["\nDATUM", self.to_yaml]
|
43
|
-
record[name] = field.coerce(data)
|
44
|
-
end
|
45
|
-
|
46
46
|
end # Field
|
47
47
|
end # Metadata
|
48
|
-
end # Rfm
|
48
|
+
end # Rfm
|
data/lib/rfm/metadata/field.rb
CHANGED
@@ -2,7 +2,7 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
module Rfm
|
4
4
|
module Metadata
|
5
|
-
|
5
|
+
|
6
6
|
# The Field object represents a single FileMaker field. It *does not hold the data* in the field. Instead,
|
7
7
|
# it serves as a source of metadata about the field. For example, if your script is trying to be highly
|
8
8
|
# dynamic about its field access, it may need to determine the data type of a field at run time. Here's
|
@@ -60,29 +60,32 @@ module Rfm
|
|
60
60
|
# The code above makes sure the control is always an array. Typically, though, you'll know up front
|
61
61
|
# if the control is an array or not, and you can code accordingly.
|
62
62
|
class Field
|
63
|
-
|
63
|
+
|
64
64
|
attr_reader :name, :result, :type, :max_repeats, :global
|
65
65
|
meta_attr_accessor :resultset_meta
|
66
66
|
def_delegator :resultset_meta, :layout_object, :layout_object
|
67
67
|
# Initializes a field object. You'll never need to do this. Instead, get your Field objects from
|
68
68
|
# Resultset::field_meta
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
69
|
+
def initialize(attributes)
|
70
|
+
if attributes && attributes.size > 0
|
71
|
+
_attach_as_instance_variables attributes
|
72
|
+
end
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
76
|
# Coerces the text value from an +fmresultset+ document into proper Ruby types based on the
|
77
77
|
# type of the field. You'll never need to do this: Rfm does it automatically for you when you
|
78
78
|
# access field data through the Record object.
|
79
79
|
def coerce(value)
|
80
80
|
case
|
81
|
-
when (value.nil? or value.empty?)
|
82
|
-
|
83
|
-
when value.is_a?(
|
81
|
+
when (value.nil? or value.empty?)
|
82
|
+
return nil
|
83
|
+
when value.is_a?(Array)
|
84
|
+
return value.collect {|v| coerce(v)}
|
85
|
+
when value.is_a?(Hash)
|
86
|
+
return coerce(value.values[0])
|
84
87
|
end
|
85
|
-
|
88
|
+
|
86
89
|
case result
|
87
90
|
when "text" then value
|
88
91
|
when "number" then BigDecimal.new(value.to_s)
|
@@ -90,39 +93,39 @@ module Rfm
|
|
90
93
|
when "time" then DateTime.strptime("1/1/-4712 #{value}", "%m/%d/%Y #{resultset_meta.time_format}")
|
91
94
|
when "timestamp" then DateTime.strptime(value, resultset_meta.timestamp_format)
|
92
95
|
when "container" then
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
96
|
+
#resultset_meta = resultset.instance_variable_get(:@meta)
|
97
|
+
if resultset_meta && resultset_meta['doctype'] && value.to_s[/\?/]
|
98
|
+
URI.parse(resultset_meta['doctype'].last.to_s).tap{|uri| uri.path, uri.query = value.split('?')}
|
99
|
+
else
|
100
|
+
value
|
101
|
+
end
|
99
102
|
else nil
|
100
103
|
end
|
101
104
|
rescue
|
102
105
|
puts("ERROR in Field#coerce:", name, value, result, resultset_meta.timestamp_format, $!)
|
103
106
|
nil
|
104
107
|
end
|
105
|
-
|
108
|
+
|
106
109
|
def get_mapped_name
|
107
|
-
|
108
|
-
|
110
|
+
#(resultset_meta && resultset_meta.layout && resultset_meta.layout.field_mapping[name]) || name
|
111
|
+
layout_object.field_mapping[name] || name
|
112
|
+
end
|
113
|
+
|
114
|
+
def field_definition_element_close_callback(cursor)
|
115
|
+
#self.resultset = cursor.top.object
|
116
|
+
#resultset_meta = resultset.instance_variable_get(:@meta)
|
117
|
+
self.resultset_meta = cursor.top.object.instance_variable_get(:@meta)
|
118
|
+
#puts ["\nFIELD#field_definition_element_close_callback", resultset_meta]
|
119
|
+
resultset_meta.field_meta[get_mapped_name.to_s.downcase] = self
|
109
120
|
end
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def relatedset_field_definition_element_close_callback(cursor)
|
120
|
-
#self.resultset = cursor.top.object
|
121
|
-
self.resultset_meta = cursor.top.object.instance_variable_get(:@meta)
|
122
|
-
cursor.parent.object[get_mapped_name.split('::').last.to_s.downcase] = self
|
123
|
-
#puts ['FIELD_portal_callback', name, cursor.parent.object.object_id, cursor.parent.tag, cursor.parent.object[name.split('::').last.to_s.downcase]].join(', ')
|
124
|
-
end
|
125
|
-
|
121
|
+
|
122
|
+
def relatedset_field_definition_element_close_callback(cursor)
|
123
|
+
#self.resultset = cursor.top.object
|
124
|
+
self.resultset_meta = cursor.top.object.instance_variable_get(:@meta)
|
125
|
+
cursor.parent.object[get_mapped_name.split('::').last.to_s.downcase] = self
|
126
|
+
#puts ['FIELD_portal_callback', name, cursor.parent.object.object_id, cursor.parent.tag, cursor.parent.object[name.split('::').last.to_s.downcase]].join(', ')
|
127
|
+
end
|
128
|
+
|
126
129
|
end # Field
|
127
130
|
end # Metadata
|
128
|
-
end # Rfm
|
131
|
+
end # Rfm
|
@@ -1,84 +1,84 @@
|
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
+
attr_reader :name, :style, :value_list_name
|
26
|
+
meta_attr_accessor :layout_meta
|
27
|
+
|
28
|
+
FIELD_CONTROL_STYLE_MAP = {
|
29
|
+
'EDITTEXT' => :edit_box,
|
30
|
+
'POPUPMENU' => :popup_menu,
|
31
|
+
'CHECKBOX' => :checkbox_set,
|
32
|
+
'RADIOBUTTONS' => :radio_button_set,
|
33
|
+
'POPUPLIST' => :popup_list,
|
34
|
+
'CALENDAR' => :calendar,
|
35
|
+
'SCROLLTEXT' => :scrollable,
|
36
|
+
}
|
37
|
+
|
38
38
|
# def initialize(_attributes, meta)
|
39
39
|
# puts ["\nFieldControl#initialize", "_attributes: #{_attributes}", "meta: #{meta.class}"]
|
40
40
|
# self.layout_meta = meta
|
41
41
|
# _attach_as_instance_variables(_attributes) if _attributes
|
42
42
|
# self
|
43
43
|
# end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
44
|
+
|
45
|
+
def initialize(meta)
|
46
|
+
#puts ["\nFieldControl#initialize", "meta: #{meta.class}"]
|
47
|
+
self.layout_meta = meta
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
# # Handle manual attachment of STYLE element.
|
52
|
+
# def handle_style_element(attributes)
|
53
|
+
# _attach_as_instance_variables attributes, :key_translator=>method(:translate_value_list_key), :value_translator=>method(:translate_style_value)
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# def translate_style_value(key, val)
|
57
|
+
# #puts ["TRANSLATE_STYLE", raw].join(', ')
|
58
|
+
# {
|
59
|
+
# 'EDITTEXT' => :edit_box,
|
60
|
+
# 'POPUPMENU' => :popup_menu,
|
61
|
+
# 'CHECKBOX' => :checkbox_set,
|
62
|
+
# 'RADIOBUTTONS' => :radio_button_set,
|
63
|
+
# 'POPUPLIST' => :popup_list,
|
64
|
+
# 'CALENDAR' => :calendar,
|
65
|
+
# 'SCROLLTEXT' => :scrollable,
|
66
|
+
# }[val] || val
|
67
|
+
# end
|
68
|
+
|
69
|
+
# def translate_value_list_key(raw)
|
70
|
+
# {'valuelist'=>'value_list_name'}[raw] || raw
|
71
|
+
# end
|
72
|
+
|
73
|
+
def value_list
|
74
|
+
layout_meta.value_lists[value_list_name]
|
75
|
+
end
|
76
76
|
|
77
77
|
def element_close_handler #(_cursor)
|
78
|
-
|
78
|
+
@type = FIELD_CONTROL_STYLE_MAP[@type] || @type
|
79
79
|
layout_meta.receive_field_control(self)
|
80
80
|
end
|
81
81
|
|
82
|
-
|
83
|
-
|
84
|
-
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,43 +1,43 @@
|
|
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
|
-
|
2
|
+
module Metadata
|
3
|
+
class LayoutMeta < CaseInsensitiveHash
|
4
|
+
|
5
|
+
def initialize(layout)
|
6
|
+
@layout = layout
|
7
|
+
end
|
8
|
+
|
9
|
+
def field_controls
|
10
|
+
self['field_controls'] ||= CaseInsensitiveHash.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def field_names
|
14
|
+
field_controls.values.collect{|v| v.name}
|
15
|
+
end
|
16
|
+
|
17
|
+
def field_keys
|
18
|
+
field_controls.keys
|
19
|
+
end
|
20
|
+
|
21
|
+
def value_lists
|
22
|
+
self['value_lists'] ||= CaseInsensitiveHash.new
|
23
|
+
end
|
24
|
+
|
25
25
|
# def handle_new_field_control(attributes)
|
26
26
|
# name = attributes['name']
|
27
27
|
# field_control = FieldControl.new(attributes, self)
|
28
28
|
# field_controls[get_mapped_name(name)] = field_control
|
29
29
|
# end
|
30
|
-
|
30
|
+
|
31
31
|
def receive_field_control(fc)
|
32
32
|
#name = fc.name
|
33
|
-
field_controls[get_mapped_name(fc.name)] = fc
|
33
|
+
field_controls[get_mapped_name(fc.name)] = fc
|
34
34
|
end
|
35
|
-
|
36
|
-
|
35
|
+
|
36
|
+
# Should this be in FieldControl object?
|
37
37
|
def get_mapped_name(name)
|
38
|
-
|
38
|
+
(@layout.field_mapping[name]) || name
|
39
39
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,75 +1,75 @@
|
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
2
|
+
module Metadata
|
3
|
+
class ResultsetMeta < CaseInsensitiveHash
|
4
|
+
|
5
|
+
def field_meta
|
6
|
+
self['field_meta'] ||= CaseInsensitiveHash.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def portal_meta
|
10
|
+
self['portal_meta'] ||= CaseInsensitiveHash.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def date_format
|
14
|
+
self['date_format']
|
15
|
+
end
|
16
|
+
|
17
|
+
def time_format
|
18
|
+
self['time_format']
|
19
|
+
end
|
20
|
+
|
21
|
+
def timestamp_format
|
22
|
+
self['timestamp_format']
|
23
|
+
end
|
24
|
+
|
25
|
+
def total_count
|
26
|
+
self['total_count'].to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def foundset_count
|
30
|
+
self['count'].to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch_size
|
34
|
+
self['fetch_size'].to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def table
|
38
|
+
self['table']
|
39
|
+
end
|
40
|
+
|
41
|
+
def error
|
42
|
+
self['error']
|
43
|
+
end
|
44
|
+
|
45
|
+
def field_names
|
46
|
+
field_meta ? field_meta.values.collect{|v| v.name} : []
|
47
|
+
end
|
48
|
+
|
49
|
+
def field_keys
|
50
|
+
field_meta ? field_meta.keys : []
|
51
|
+
end
|
52
|
+
|
53
|
+
def portal_names
|
54
|
+
portal_meta ? portal_meta.keys : []
|
55
|
+
end
|
56
|
+
|
57
|
+
# def handle_new_field(attributes)
|
58
|
+
# f = Field.new(attributes)
|
59
|
+
# # TODO: Re-enable these when you stop using the before_close callback.
|
60
|
+
# # name = attributes['name']
|
61
|
+
# # self[name] = f
|
62
|
+
# end
|
63
|
+
|
64
|
+
def layout_object
|
65
|
+
self['layout_object']
|
66
|
+
end
|
67
|
+
|
68
|
+
def attach_layout_object_from_cursor(cursor)
|
69
|
+
self['layout_object'] = cursor.top.object.layout
|
70
|
+
#puts ["\nRESULTSET_META#metadata_element_close_callback", self['layout_object']]
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|