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/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
|