ginjo-rfm 3.0.4 → 3.0.5
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 +8 -8
- data/CHANGELOG.md +15 -0
- data/lib/rfm.rb +19 -16
- data/lib/rfm/VERSION +1 -1
- data/lib/rfm/metadata/datum.rb +7 -4
- data/lib/rfm/metadata/field.rb +32 -17
- data/lib/rfm/metadata/field_control.rb +44 -46
- data/lib/rfm/metadata/layout_meta.rb +10 -5
- data/lib/rfm/metadata/resultset_meta.rb +15 -6
- data/lib/rfm/record.rb +1 -1
- data/lib/rfm/resultset.rb +8 -6
- data/lib/rfm/utilities/sax/fmpxml_minimal.yml +5 -0
- data/lib/rfm/utilities/sax/fmpxmllayout.yml +27 -11
- data/lib/rfm/utilities/sax/fmresultset.yml +24 -18
- data/lib/rfm/utilities/sax_parser.rb +451 -308
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NWQ1ODlhYTI5ZDM2OTkxNDc3MGE5MGEyZjM4YWI2YTA0MzIyMTM1OA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzgwMmY2OTA3OTZkMzYxZTQ2ZmE2NzY1YmFkYmJhMzUxOGEwMGNlYw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NzVmNWUwZmNlNTAxNmIwNzNlNDkxMDU4NTE3YWY1YzU1ZDg0MDIzZjFhNWVj
|
10
|
+
MTNkZmJhN2QxNDBmOTY4NzE1MzRhMjU4MjIzNGZlODNlZjVhYmVlNTUwMWRk
|
11
|
+
YzU0ZDBkNDRhNjk3MjFhNDBjNzRmNjBjODU5NmY4MmQ1MjgwZjE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODZlYmU4ZTAwMmI3M2FjZDA0ZjE5MDk0NzNhNmNkODJmOTc5NmUxZmI0YzU5
|
14
|
+
OTUxNTM4OTUxZjA5NTdkOTE0ZTZlNjAxMGI3NWMxY2FiOTJmNTJhODQxNDhh
|
15
|
+
N2IxN2VjOWQ0ZDlhMTU0NzNjYjU3NGI3ZTJhOTk0NDM3MGQ1NDM=
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## Ginjo-Rfm 3.0.5
|
4
|
+
|
5
|
+
* Fixed parser handling of `<field>` element that's missing a `<data>` element.
|
6
|
+
* Fixed coercion of repeating field data.
|
7
|
+
* Fixed case where special characters in Filemaker data yielded array instead of string (sax parsing split text).
|
8
|
+
* Fixed various bugs in metadata parsing.
|
9
|
+
* Detached resultset from Rfm::MetaData::Field instance, now attaching only ResultsetMeta to Field.
|
10
|
+
* Fixed ruby-prof rake task.
|
11
|
+
* Updated deprecated rspec 2 specs, will now work with rspec 2 or 3.
|
12
|
+
* Added more specs for some recently found bugs and for sax parser.
|
13
|
+
* Fixed broken ActiveModel Lint specs in Ruby 2.1.
|
14
|
+
* General refinements & cleanup.
|
15
|
+
* Optimizations to sax_parser.
|
16
|
+
* Fixed typo in field.rb that was causing bugs.
|
17
|
+
|
3
18
|
## Ginjo-Rfm 3.0.4
|
4
19
|
|
5
20
|
* Corrected reference to @meta in fmpxmllayout.yml. Specs now passing for Layout#load\_layout.
|
data/lib/rfm.rb
CHANGED
@@ -59,12 +59,8 @@ module Rfm
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def_delegators 'Rfm::Factory', :servers, :server, :db, :database, :layout
|
62
|
-
def_delegators 'Rfm::SaxParser', :backend, :backend=
|
63
|
-
def_delegators 'Rfm::SaxParser::Handler', :get_backend
|
64
62
|
def_delegators 'Rfm::Config', :config, :get_config, :config_clear
|
65
63
|
def_delegators 'Rfm::Resultset', :load_data
|
66
|
-
alias_method :parser, :backend
|
67
|
-
alias_method :parser=, :backend=
|
68
64
|
|
69
65
|
def models(*args)
|
70
66
|
Rfm::Base
|
@@ -75,9 +71,7 @@ module Rfm
|
|
75
71
|
Rfm::Base
|
76
72
|
Rfm::Factory.modelize(*args)
|
77
73
|
end
|
78
|
-
|
79
|
-
#attr_accessor :log
|
80
|
-
|
74
|
+
|
81
75
|
def logger
|
82
76
|
@@logger ||= get_config[:logger] || Logger.new(STDOUT).tap {|l| l.formatter = proc {|severity, datetime, progname, msg| "#{datetime}: Rfm-#{severity} #{msg}\n"}}
|
83
77
|
end
|
@@ -88,17 +82,26 @@ module Rfm
|
|
88
82
|
@@logger = obj
|
89
83
|
end
|
90
84
|
|
85
|
+
# DEFAULT_CLASS = CaseInsensitiveHash
|
86
|
+
# TEMPLATE_PREFIX = File.join(File.dirname(__FILE__), 'rfm/utilities/sax/')
|
87
|
+
# TEMPLATES = {
|
88
|
+
# :fmpxmllayout => 'fmpxmllayout.yml',
|
89
|
+
# :fmresultset => 'fmresultset.yml',
|
90
|
+
# :fmpxmlresult => 'fmpxmlresult.yml',
|
91
|
+
# :none => nil
|
92
|
+
# }
|
91
93
|
|
94
|
+
PARSER_DEFAULTS = {
|
95
|
+
:default_class => CaseInsensitiveHash,
|
96
|
+
:template_prefix => File.join(File.dirname(__FILE__), 'rfm/utilities/sax/'),
|
97
|
+
:templates => {
|
98
|
+
:fmpxmllayout => 'fmpxmllayout.yml',
|
99
|
+
:fmresultset => 'fmresultset.yml',
|
100
|
+
:fmpxmlresult => 'fmpxmlresult.yml',
|
101
|
+
:none => nil
|
102
|
+
}
|
103
|
+
}
|
92
104
|
|
93
105
|
extend self
|
94
106
|
|
95
|
-
SaxParser.default_class = CaseInsensitiveHash
|
96
|
-
SaxParser.template_prefix = File.join(File.dirname(__FILE__), 'rfm/utilities/sax/')
|
97
|
-
SaxParser.templates.merge!({
|
98
|
-
:fmpxmllayout => 'fmpxmllayout.yml',
|
99
|
-
:fmresultset => 'fmresultset.yml',
|
100
|
-
:fmpxmlresult => 'fmpxmlresult.yml',
|
101
|
-
:none => nil
|
102
|
-
})
|
103
|
-
|
104
107
|
end # Rfm
|
data/lib/rfm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0.
|
1
|
+
3.0.5
|
data/lib/rfm/metadata/datum.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
-
require 'delegate'
|
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
|
+
#puts ["\nDATUM#get_mapped_name", "name: #{name}", "mapping: #{resultset.layout.field_mapping.to_yaml}"]
|
8
9
|
(resultset && resultset.layout && resultset.layout.field_mapping[name]) || name
|
9
10
|
end
|
10
11
|
|
12
|
+
# NOT sure what this method is for. Can't find a reference to it.
|
11
13
|
def main_callback(cursor)
|
12
14
|
resultset = cursor.top.object
|
13
15
|
name = get_mapped_name(@attributes['name'].to_s, resultset)
|
@@ -16,7 +18,7 @@ module Rfm
|
|
16
18
|
cursor.parent.object[name.downcase] = field.coerce(data)
|
17
19
|
end
|
18
20
|
|
19
|
-
def
|
21
|
+
def portal_field_element_close_callback(cursor)
|
20
22
|
resultset = cursor.top.object
|
21
23
|
table, name = @attributes['name'].to_s.split('::')
|
22
24
|
#puts ['DATUM_portal_callback_01', table, name].join(', ')
|
@@ -29,14 +31,15 @@ module Rfm
|
|
29
31
|
end
|
30
32
|
|
31
33
|
# Should return value only.
|
32
|
-
def
|
34
|
+
def field_element_close_callback(cursor)
|
33
35
|
record = cursor.parent.object
|
34
36
|
resultset = cursor.top.object
|
35
37
|
|
36
38
|
name = get_mapped_name(@attributes['name'].to_s, resultset)
|
37
39
|
field = resultset.field_meta[name]
|
38
|
-
data = @attributes['data']
|
40
|
+
data = @attributes['data'] #'data'
|
39
41
|
#puts ["\nDATUM", name, record.class, resultset.class, data]
|
42
|
+
#puts ["\nDATUM", self.to_yaml]
|
40
43
|
record[name] = field.coerce(data)
|
41
44
|
end
|
42
45
|
|
data/lib/rfm/metadata/field.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Rfm
|
2
4
|
module Metadata
|
3
5
|
|
4
6
|
# The Field object represents a single FileMaker field. It *does not hold the data* in the field. Instead,
|
5
|
-
# it serves as a source of metadata about the field. For example, if
|
7
|
+
# it serves as a source of metadata about the field. For example, if your script is trying to be highly
|
6
8
|
# dynamic about its field access, it may need to determine the data type of a field at run time. Here's
|
7
9
|
# how:
|
8
10
|
#
|
@@ -60,11 +62,14 @@ module Rfm
|
|
60
62
|
class Field
|
61
63
|
|
62
64
|
attr_reader :name, :result, :type, :max_repeats, :global
|
63
|
-
meta_attr_accessor :
|
65
|
+
meta_attr_accessor :resultset_meta
|
66
|
+
def_delegator :resultset_meta, :layout_object, :layout_object
|
64
67
|
# Initializes a field object. You'll never need to do this. Instead, get your Field objects from
|
65
68
|
# Resultset::field_meta
|
66
69
|
def initialize(attributes)
|
67
|
-
|
70
|
+
if attributes && attributes.size > 0
|
71
|
+
_attach_as_instance_variables attributes
|
72
|
+
end
|
68
73
|
self
|
69
74
|
end
|
70
75
|
|
@@ -72,15 +77,20 @@ module Rfm
|
|
72
77
|
# type of the field. You'll never need to do this: Rfm does it automatically for you when you
|
73
78
|
# access field data through the Record object.
|
74
79
|
def coerce(value)
|
75
|
-
|
80
|
+
case
|
81
|
+
when (value.nil? or value.empty?); return nil
|
82
|
+
when value.is_a?(Array); return value.collect {|v| coerce(v)}
|
83
|
+
when value.is_a?(Hash); return coerce(value.values[0])
|
84
|
+
end
|
85
|
+
|
76
86
|
case result
|
77
87
|
when "text" then value
|
78
88
|
when "number" then BigDecimal.new(value.to_s)
|
79
|
-
when "date" then Date.strptime(value,
|
80
|
-
when "time" then DateTime.strptime("1/1/-4712 #{value}", "%m/%d/%Y #{
|
81
|
-
when "timestamp" then DateTime.strptime(value,
|
89
|
+
when "date" then Date.strptime(value, resultset_meta.date_format)
|
90
|
+
when "time" then DateTime.strptime("1/1/-4712 #{value}", "%m/%d/%Y #{resultset_meta.time_format}")
|
91
|
+
when "timestamp" then DateTime.strptime(value, resultset_meta.timestamp_format)
|
82
92
|
when "container" then
|
83
|
-
resultset_meta = resultset.instance_variable_get(:@meta)
|
93
|
+
#resultset_meta = resultset.instance_variable_get(:@meta)
|
84
94
|
if resultset_meta && resultset_meta['doctype'] && value.to_s[/\?/]
|
85
95
|
URI.parse(resultset_meta['doctype'].last.to_s).tap{|uri| uri.path, uri.query = value.split('?')}
|
86
96
|
else
|
@@ -88,22 +98,27 @@ module Rfm
|
|
88
98
|
end
|
89
99
|
else nil
|
90
100
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
101
|
+
rescue
|
102
|
+
puts("ERROR in Field#coerce:", name, value, result, resultset_meta.timestamp_format, $!)
|
103
|
+
nil
|
94
104
|
end
|
95
105
|
|
96
106
|
def get_mapped_name
|
97
|
-
(
|
107
|
+
#(resultset_meta && resultset_meta.layout && resultset_meta.layout.field_mapping[name]) || name
|
108
|
+
layout_object.field_mapping[name] || name
|
98
109
|
end
|
99
110
|
|
100
|
-
def
|
101
|
-
self.resultset = cursor.top.object
|
102
|
-
|
111
|
+
def field_definition_element_close_callback(cursor)
|
112
|
+
#self.resultset = cursor.top.object
|
113
|
+
#resultset_meta = resultset.instance_variable_get(:@meta)
|
114
|
+
self.resultset_meta = cursor.top.object.instance_variable_get(:@meta)
|
115
|
+
#puts ["\nFIELD#field_definition_element_close_callback", resultset_meta]
|
116
|
+
resultset_meta.field_meta[get_mapped_name.to_s.downcase] = self
|
103
117
|
end
|
104
118
|
|
105
|
-
def
|
106
|
-
self.resultset = cursor.top.object
|
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)
|
107
122
|
cursor.parent.object[get_mapped_name.split('::').last.to_s.downcase] = self
|
108
123
|
#puts ['FIELD_portal_callback', name, cursor.parent.object.object_id, cursor.parent.tag, cursor.parent.object[name.split('::').last.to_s.downcase]].join(', ')
|
109
124
|
end
|
@@ -24,62 +24,60 @@ module Rfm
|
|
24
24
|
class FieldControl
|
25
25
|
attr_reader :name, :style, :value_list_name
|
26
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
|
+
}
|
27
37
|
|
28
|
-
|
38
|
+
# def initialize(_attributes, meta)
|
39
|
+
# puts ["\nFieldControl#initialize", "_attributes: #{_attributes}", "meta: #{meta.class}"]
|
40
|
+
# self.layout_meta = meta
|
41
|
+
# _attach_as_instance_variables(_attributes) if _attributes
|
42
|
+
# self
|
43
|
+
# end
|
44
|
+
|
45
|
+
def initialize(meta)
|
46
|
+
#puts ["\nFieldControl#initialize", "meta: #{meta.class}"]
|
29
47
|
self.layout_meta = meta
|
30
|
-
_attach_as_instance_variables attributes
|
31
48
|
self
|
32
49
|
end
|
33
50
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
38
68
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
'EDITTEXT' => :edit_box,
|
43
|
-
'POPUPMENU' => :popup_menu,
|
44
|
-
'CHECKBOX' => :checkbox_set,
|
45
|
-
'RADIOBUTTONS' => :radio_button_set,
|
46
|
-
'POPUPLIST' => :popup_list,
|
47
|
-
'CALENDAR' => :calendar,
|
48
|
-
'SCROLLTEXT' => :scrollable,
|
49
|
-
}[raw] || raw
|
50
|
-
end
|
51
|
-
|
52
|
-
def translate_value_list_key(raw)
|
53
|
-
{'valuelist'=>'value_list_name'}[raw] || raw
|
54
|
-
end
|
69
|
+
# def translate_value_list_key(raw)
|
70
|
+
# {'valuelist'=>'value_list_name'}[raw] || raw
|
71
|
+
# end
|
55
72
|
|
56
73
|
def value_list
|
57
74
|
layout_meta.value_lists[value_list_name]
|
58
75
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
# @style = :edit_box
|
65
|
-
# when "POPUPMENU"
|
66
|
-
# @style = :popup_menu
|
67
|
-
# when "CHECKBOX"
|
68
|
-
# @style = :checkbox_set
|
69
|
-
# when "RADIOBUTTONS"
|
70
|
-
# @style = :radio_button_set
|
71
|
-
# when "POPUPLIST"
|
72
|
-
# @style = :popup_list
|
73
|
-
# when "CALENDAR"
|
74
|
-
# @style = :calendar
|
75
|
-
# when "SCROLLTEXT"
|
76
|
-
# @style = :scrollable
|
77
|
-
# else
|
78
|
-
# nil
|
79
|
-
# end
|
80
|
-
# @value_list_name = value_list_name
|
81
|
-
# rfm_metaclass.instance_variable_set :@value_list, value_list
|
82
|
-
# end
|
76
|
+
|
77
|
+
def element_close_handler #(_cursor)
|
78
|
+
@type = FIELD_CONTROL_STYLE_MAP[@type] || @type
|
79
|
+
layout_meta.receive_field_control(self)
|
80
|
+
end
|
83
81
|
|
84
82
|
end
|
85
83
|
end
|
@@ -22,11 +22,16 @@ module Rfm
|
|
22
22
|
self['value_lists'] ||= CaseInsensitiveHash.new
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
# def handle_new_field_control(attributes)
|
26
|
+
# name = attributes['name']
|
27
|
+
# field_control = FieldControl.new(attributes, self)
|
28
|
+
# field_controls[get_mapped_name(name)] = field_control
|
29
|
+
# end
|
30
|
+
|
31
|
+
def receive_field_control(fc)
|
32
|
+
#name = fc.name
|
33
|
+
field_controls[get_mapped_name(fc.name)] = fc
|
34
|
+
end
|
30
35
|
|
31
36
|
# Should this be in FieldControl object?
|
32
37
|
def get_mapped_name(name)
|
@@ -54,12 +54,21 @@ module Rfm
|
|
54
54
|
portal_meta ? portal_meta.keys : []
|
55
55
|
end
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
63
72
|
|
64
73
|
end
|
65
74
|
end
|
data/lib/rfm/record.rb
CHANGED
data/lib/rfm/resultset.rb
CHANGED
@@ -49,9 +49,9 @@ module Rfm
|
|
49
49
|
|
50
50
|
|
51
51
|
class << self
|
52
|
-
def load_data(data)
|
52
|
+
def load_data(data, object=self.new)
|
53
53
|
Rfm::Connection
|
54
|
-
Rfm::SaxParser.parse(data, :fmresultset,
|
54
|
+
Rfm::SaxParser.parse(data, :fmresultset, object)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -111,6 +111,7 @@ module Rfm
|
|
111
111
|
@meta ||= Metadata::ResultsetMeta.new
|
112
112
|
end
|
113
113
|
|
114
|
+
# Deprecated on 7/29/2014. Stop using.
|
114
115
|
def handle_new_record(attributes)
|
115
116
|
r = Rfm::Record.new(self, attributes, {})
|
116
117
|
self << r
|
@@ -118,14 +119,15 @@ module Rfm
|
|
118
119
|
end
|
119
120
|
|
120
121
|
def end_datasource_element_callback(cursor)
|
121
|
-
%w(date_format time_format timestamp_format).each{|f| convert_date_time_format(
|
122
|
+
%w(date_format time_format timestamp_format).each{|f| convert_date_time_format(send(f))}
|
123
|
+
@meta.attach_layout_object_from_cursor(cursor)
|
122
124
|
end
|
123
125
|
|
124
126
|
private
|
125
127
|
|
126
|
-
def check_for_errors(
|
127
|
-
#puts ["\nRESULTSET#check_for_errors",
|
128
|
-
raise Rfm::Error.getError(
|
128
|
+
def check_for_errors(error_code=@meta['error'].to_i, raise_401=state[:raise_401])
|
129
|
+
#puts ["\nRESULTSET#check_for_errors", "meta[:error] #{@meta[:error]}", "error_code: #{error_code}", "raise_401: #{raise_401}"]
|
130
|
+
raise Rfm::Error.getError(error_code) if error_code != 0 && (error_code != 401 || raise_401)
|
129
131
|
end
|
130
132
|
|
131
133
|
def convert_date_time_format(fm_format)
|