ginjo-rfm 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## Ginjo-Rfm 2.1.2
4
+
5
+ * Fixed config.rb so that :file_path (to user-defined yml config file) can be specified as a single path string
6
+ or as an array of path strings.
7
+
8
+ ## Ginjo-Rfm 2.1.1
9
+
10
+ * Bug fixes
11
+
12
+ * Specs passing in Ruby 1.8.7, 1.9.2.
13
+
3
14
  ## Ginjo-Rfm 2.1.0
4
15
 
5
16
  * Removed `:include_portals` query option in favor of `:ignore_portals`.
@@ -15,11 +26,9 @@
15
26
  * Added grammar translation layer between xml parser and Rfm, allowing all supported xml grammars to be used with Rfm.
16
27
  This will also streamline changes/additions to Filemaker's xml grammar(s).
17
28
 
18
- * Added ability to manually import fmprestulset and fmpxmlresult data (from file, variable, etc.).
19
-
20
29
  * Fixed case statement for ruby 1.9
21
30
 
22
- * Configuration `:use` option is now works for all Rfm objects that respond to `config`.
31
+ * Configuration `:use` option now works for all Rfm objects that respond to `config`.
23
32
 
24
33
  ## Ginjo-Rfm 2.0.2
25
34
 
data/README.md CHANGED
@@ -15,19 +15,35 @@ Rfm is a Ruby-Filemaker adapter, a Ruby gem that allows scripts and applications
15
15
 
16
16
  ## New in version 2.1
17
17
 
18
- ginjo-rfm 2.1 is a combination of new features, bug fixes, and a lot of code refactoring.
18
+ Ginjo-rfm 2.1 has a combination of new features, bug fixes, and a lot of code refactoring.
19
19
  Most api calls remain the same, but a good deal of underlying code has been transformed
20
- to support, and take advantage of, the progress of technologies surrounding Ruby.
20
+ to support and take advantage of the progress of technologies surrounding Ruby.
21
21
 
22
22
  * Portals are now included by default.
23
23
  Removed `:include_portals` query option in favor of `:ignore_portals`.
24
24
  Added `:max_portal_rows` query option.
25
25
  * Added field-remapping framework to allow model fields with different names than Filemaker fields.
26
+
27
+ class User < Rfm::Base
28
+ config :field_mapping => {
29
+ #<filemaker-field-name> => <rfm-field-name>
30
+ 'userName' => 'login',
31
+ 'First Name' => 'first_name',
32
+ 'Last Name' => 'last_name',
33
+ 'IDperson' => 'person_id'
34
+ }
35
+ end
36
+
37
+ User.find(:login=>'bill') # => [{'login' => 'bill', 'first_name' => 'Bill', ...}, ...]
38
+
26
39
  * Fixed date/time/timestamp translations when writing data to Filemaker.
27
40
  * Detached new Server objects from Factory.servers hash, so wont reuse or stack-up servers.
28
41
  * Added grammar translation layer between xml parser and Rfm, allowing all supported xml grammars to be used with Rfm.
29
42
  This will also streamline changes/additions to Filemaker's xml grammar(s).
30
43
  * Added ability to manually import fmpresultset and fmpxmlresult data (from file, variable, etc.).
44
+
45
+ Rfm::Resultset.load_data(file_or_string).
46
+
31
47
  * Compatibility fixes for ruby 1.9.
32
48
  * Configuration `:use` option now works for all Rfm objects that respond to `config`.
33
49
 
@@ -179,7 +195,7 @@ Get the total count of all records in the table
179
195
 
180
196
  MyModel.total_count
181
197
 
182
- Get the portal names (table-occurence names) on the current layout
198
+ Get the portal names (table-occurrence names) on the current layout
183
199
 
184
200
  MyModel.portal_names
185
201
 
@@ -689,26 +705,30 @@ To delete the record whose recid is 200:
689
705
 
690
706
  my_layout.delete(200)
691
707
 
692
- All of these methods return an Rfm::Result::ResultSet object (see below), and every one of them takes an optional parameter (the very last one) with additional options. For example, to find just a page full of records, you can do this:
708
+ All of these methods return an Rfm::Resultset object (see below), and every one of them takes an optional parameter (the very last one) with additional options. For example, to find just a page full of records, you can do this:
693
709
 
694
710
  my_layout.find({:state => "AZ"}, {:max_records => 10, :skip_records => 100})
695
711
 
696
- For a complete list of the available options, see the "expand_options" method in the Rfm::Server object in the file named rfm_command.rb.
712
+ For a complete list of the available options, see the "expand_options" method in the Rfm::Server object in the file named server.rb.
697
713
 
698
- Finally, if filemaker returns an error when executing any of these methods, an error will be raised in your ruby script. There is one exception to this, though. If a find results in no records being found (FileMaker error # 401) I just ignore it and return you a ResultSet with zero records in it. If you prefer an error in this case, add :raise_on_401 => true to the options you pass the Rfm::Server when you create it.
714
+ Finally, if filemaker returns an error when executing any of these methods, an error will be raised in your ruby script. There is one exception to this, though. If a find results in no records being found (FileMaker error # 401) I just ignore it and return you a Resultset with zero records in it. If you prefer an error in this case, add :raise_on_401 => true to the options you pass the Rfm::Server when you create it.
699
715
 
700
716
 
701
- ### ResultSet and Record Objects
717
+ ### Resultset and Record Objects
702
718
 
703
- Any method on the Layout object that returns data will return a ResultSet object. Rfm::Result::ResultSet is a subclass of Array, so first and foremost, you can use it like any other array:
719
+ Any method on the Layout object that returns data will return a Resultset object. Rfm::Resultset is a subclass of Array, so first and foremost, you can use it like any other array:
704
720
 
705
721
  my_result = my_layout.any
706
722
  my_result.size # returns '1'
707
- my_result[0] # returns the first record (an Rfm::Result::Record object)
723
+ my_result[0] # returns the first record (an Rfm::Record object)
708
724
 
709
- The ResultSet object also tells you information about the fields and portals in the result. ResultSet#fields and ResultSet#portals are both standard ruby hashes, with strings for keys. The fields hash has Rfm::Result::Field objects for values. The portals hash has another hash for its values. This nested hash is the fields on the portal. This would print out all the field names:
725
+ The Resultset object also tells you information about the fields and portals in the result. Resultset#field\_meta and Resultset#portal\_meta are both standard ruby hashes, with strings for keys. The fields hash has Rfm::Metadata::Field objects for values. The portals hash has another hash for its values. This nested hash is the fields on the portal. This would print out all the field names:
710
726
 
711
- my_result.fields.each { |name, field| puts name }
727
+ my_result.field_meta.each { |name, field| puts name }
728
+
729
+ Or, as a convenience, you can do this:
730
+
731
+ my_result.field_names
712
732
 
713
733
  This would print out the tables each portal on the layout is associated with. Below each table name, and indented, it will print the names of all the fields on each portal.
714
734
 
@@ -717,7 +737,11 @@ This would print out the tables each portal on the layout is associated with. Be
717
737
  fields.each { |name, field| puts "\t#{name}"}
718
738
  }
719
739
 
720
- But most importantly, the ResultSet contains record objects. Rfm::Result::Record is a subclass of Hash, so it can be used in many standard ways. This code would print the value in the 'first_name' field in the first record of the ResultSet:
740
+ Also as a convenience, you can do this:
741
+
742
+ my_result.portal_names
743
+
744
+ But most importantly, the Resultset contains record objects. Rfm::Record is a subclass of Hash, so it can be used in many standard ways. This code would print the value in the 'first_name' field in the first record of the Resultset:
721
745
 
722
746
  my_record = my_result[0]
723
747
  puts my_record["first_name"]
@@ -726,7 +750,7 @@ As a convenience, if your field names are valid ruby method names (ie, they don'
726
750
 
727
751
  puts my_record.first_name
728
752
 
729
- Since ResultSets are arrays and Records are hashes, you can take advantage of Ruby's wonderful expressiveness. For example, to get a comma-separated list of the full names of all the people in California, you could do this:
753
+ Since Resultsets are arrays and Records are hashes, you can take advantage of Ruby's wonderful expressiveness. For example, to get a comma-separated list of the full names of all the people in California, you could do this:
730
754
 
731
755
  my_layout.find(:state => 'CA').collect {|rec| "#{rec.first_name} #{rec.last_name}"}.join(", ")
732
756
 
@@ -746,7 +770,7 @@ If you want to detect concurrent modification, you can do this instead:
746
770
 
747
771
  This version will refuse to update the database and raise an error if the record was modified after it was loaded but before it was saved.
748
772
 
749
- Record objects also have portals. While the portals in a ResultSet tell you about the tables and fields the portals show, the portals in a Record have the actual data. For example, if an Order record has Line Item records, you could do this:
773
+ Record objects also have portals. While the portals in a Resultset tell you about the tables and fields the portals show, the portals in a Record have the actual data. For example, if an Order record has Line Item records, you could do this:
750
774
 
751
775
  my_order = order_layout.any[0] # the [0] is important!
752
776
  my_lines = my_order.portals["Line Items"]
@@ -1,2 +1 @@
1
- 2.1.1
2
-
1
+ 2.1.2
@@ -124,9 +124,9 @@ module Rfm
124
124
  def get_config_file
125
125
  @@config_file_data ||= (
126
126
  config_file_name = @config[:file_name] || (RFM_CONFIG[:file_name] rescue nil) || 'rfm.yml'
127
- config_file_paths = [''] | (@config[:file_path] || (RFM_CONFIG[:file_path] rescue nil) || %w( config/ ))
128
- config_file_paths.collect do |f|
129
- (YAML.load_file("#{f}#{config_file_name}") rescue {})
127
+ config_file_paths = [''] | [(@config[:file_path] || (RFM_CONFIG[:file_path] rescue nil) || %w( config/ ))].flatten
128
+ config_file_paths.collect do |path|
129
+ (YAML.load_file(File.join(path, config_file_name)) rescue {})
130
130
  end.inject({}){|h,a| h.merge(a)}
131
131
  ) || {}
132
132
  end
metadata CHANGED
@@ -1,15 +1,10 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ginjo-rfm
3
- version: !ruby/object:Gem::Version
4
- hash: 9
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.1.2
5
5
  prerelease:
6
- segments:
7
- - 2
8
- - 1
9
- - 1
10
- version: 2.1.1
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Geoff Coffey
14
9
  - Mufaddal Khumri
15
10
  - Atsushi Matsuo
@@ -18,193 +13,212 @@ authors:
18
13
  autorequire:
19
14
  bindir: bin
20
15
  cert_chain: []
21
-
22
- date: 2013-03-21 00:00:00 Z
23
- dependencies:
24
- - !ruby/object:Gem::Dependency
16
+ date: 2013-03-31 00:00:00.000000000 Z
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
25
19
  name: activesupport
26
- prerelease: false
27
- requirement: &id001 !ruby/object:Gem::Requirement
20
+ requirement: !ruby/object:Gem::Requirement
28
21
  none: false
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- hash: 9
33
- segments:
34
- - 2
35
- - 3
36
- - 5
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
37
25
  version: 2.3.5
38
26
  type: :runtime
39
- version_requirements: *id001
40
- - !ruby/object:Gem::Dependency
41
- name: activemodel
42
27
  prerelease: false
43
- requirement: &id002 !ruby/object:Gem::Requirement
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.5
34
+ - !ruby/object:Gem::Dependency
35
+ name: activemodel
36
+ requirement: !ruby/object:Gem::Requirement
44
37
  none: false
45
- requirements:
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- hash: 3
49
- segments:
50
- - 0
51
- version: "0"
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
52
42
  type: :development
53
- version_requirements: *id002
54
- - !ruby/object:Gem::Dependency
55
- name: rake
56
43
  prerelease: false
57
- requirement: &id003 !ruby/object:Gem::Requirement
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ - !ruby/object:Gem::Dependency
51
+ name: rake
52
+ requirement: !ruby/object:Gem::Requirement
58
53
  none: false
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- hash: 3
63
- segments:
64
- - 0
65
- version: "0"
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
66
58
  type: :development
67
- version_requirements: *id003
68
- - !ruby/object:Gem::Dependency
69
- name: rdoc
70
59
  prerelease: false
71
- requirement: &id004 !ruby/object:Gem::Requirement
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ - !ruby/object:Gem::Dependency
67
+ name: rdoc
68
+ requirement: !ruby/object:Gem::Requirement
72
69
  none: false
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- hash: 3
77
- segments:
78
- - 0
79
- version: "0"
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
80
74
  type: :development
81
- version_requirements: *id004
82
- - !ruby/object:Gem::Dependency
83
- name: rspec
84
75
  prerelease: false
85
- requirement: &id005 !ruby/object:Gem::Requirement
76
+ version_requirements: !ruby/object:Gem::Requirement
86
77
  none: false
87
- requirements:
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec
84
+ requirement: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
88
87
  - - ~>
89
- - !ruby/object:Gem::Version
90
- hash: 27
91
- segments:
92
- - 1
93
- - 3
94
- - 0
88
+ - !ruby/object:Gem::Version
95
89
  version: 1.3.0
96
90
  type: :development
97
- version_requirements: *id005
98
- - !ruby/object:Gem::Dependency
99
- name: diff-lcs
100
91
  prerelease: false
101
- requirement: &id006 !ruby/object:Gem::Requirement
92
+ version_requirements: !ruby/object:Gem::Requirement
102
93
  none: false
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- hash: 3
107
- segments:
108
- - 0
109
- version: "0"
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: 1.3.0
98
+ - !ruby/object:Gem::Dependency
99
+ name: diff-lcs
100
+ requirement: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
110
106
  type: :development
111
- version_requirements: *id006
112
- - !ruby/object:Gem::Dependency
113
- name: yard
114
107
  prerelease: false
115
- requirement: &id007 !ruby/object:Gem::Requirement
108
+ version_requirements: !ruby/object:Gem::Requirement
116
109
  none: false
117
- requirements:
118
- - - ">="
119
- - !ruby/object:Gem::Version
120
- hash: 3
121
- segments:
122
- - 0
123
- version: "0"
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ - !ruby/object:Gem::Dependency
115
+ name: yard
116
+ requirement: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
124
122
  type: :development
125
- version_requirements: *id007
126
- - !ruby/object:Gem::Dependency
127
- name: redcarpet
128
123
  prerelease: false
129
- requirement: &id008 !ruby/object:Gem::Requirement
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ - !ruby/object:Gem::Dependency
131
+ name: redcarpet
132
+ requirement: !ruby/object:Gem::Requirement
130
133
  none: false
131
- requirements:
132
- - - ">="
133
- - !ruby/object:Gem::Version
134
- hash: 3
135
- segments:
136
- - 0
137
- version: "0"
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
138
  type: :development
139
- version_requirements: *id008
140
- - !ruby/object:Gem::Dependency
141
- name: libxml-ruby
142
139
  prerelease: false
143
- requirement: &id009 !ruby/object:Gem::Requirement
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ - !ruby/object:Gem::Dependency
147
+ name: libxml-ruby
148
+ requirement: !ruby/object:Gem::Requirement
144
149
  none: false
145
- requirements:
146
- - - ">="
147
- - !ruby/object:Gem::Version
148
- hash: 3
149
- segments:
150
- - 0
151
- version: "0"
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
152
154
  type: :development
153
- version_requirements: *id009
154
- - !ruby/object:Gem::Dependency
155
- name: nokogiri
156
155
  prerelease: false
157
- requirement: &id010 !ruby/object:Gem::Requirement
156
+ version_requirements: !ruby/object:Gem::Requirement
158
157
  none: false
159
- requirements:
160
- - - ">="
161
- - !ruby/object:Gem::Version
162
- hash: 3
163
- segments:
164
- - 0
165
- version: "0"
158
+ requirements:
159
+ - - ! '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ - !ruby/object:Gem::Dependency
163
+ name: nokogiri
164
+ requirement: !ruby/object:Gem::Requirement
165
+ none: false
166
+ requirements:
167
+ - - ! '>='
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
166
170
  type: :development
167
- version_requirements: *id010
168
- - !ruby/object:Gem::Dependency
169
- name: hpricot
170
171
  prerelease: false
171
- requirement: &id011 !ruby/object:Gem::Requirement
172
+ version_requirements: !ruby/object:Gem::Requirement
172
173
  none: false
173
- requirements:
174
- - - ">="
175
- - !ruby/object:Gem::Version
176
- hash: 3
177
- segments:
178
- - 0
179
- version: "0"
174
+ requirements:
175
+ - - ! '>='
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ - !ruby/object:Gem::Dependency
179
+ name: hpricot
180
+ requirement: !ruby/object:Gem::Requirement
181
+ none: false
182
+ requirements:
183
+ - - ! '>='
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
180
186
  type: :development
181
- version_requirements: *id011
182
- - !ruby/object:Gem::Dependency
183
- name: ox
184
187
  prerelease: false
185
- requirement: &id012 !ruby/object:Gem::Requirement
188
+ version_requirements: !ruby/object:Gem::Requirement
186
189
  none: false
187
- requirements:
188
- - - ">="
189
- - !ruby/object:Gem::Version
190
- hash: 3
191
- segments:
192
- - 0
193
- version: "0"
190
+ requirements:
191
+ - - ! '>='
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: ox
196
+ requirement: !ruby/object:Gem::Requirement
197
+ none: false
198
+ requirements:
199
+ - - ! '>='
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
194
202
  type: :development
195
- version_requirements: *id012
196
- description: Rfm lets your Ruby scripts and Rails applications talk directly to your Filemaker server. Ginjo-rfm includes ActiveModel compatibility, multiple XML parsers, compound Filemaker find requests, and a configuration API.
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ none: false
206
+ requirements:
207
+ - - ! '>='
208
+ - !ruby/object:Gem::Version
209
+ version: '0'
210
+ description: Rfm lets your Ruby scripts and Rails applications talk directly to your
211
+ Filemaker server. Ginjo-rfm includes ActiveModel compatibility, multiple XML parsers,
212
+ compound Filemaker find requests, and a configuration API.
197
213
  email: http://groups.google.com/group/rfmcommunity
198
214
  executables: []
199
-
200
215
  extensions: []
201
-
202
- extra_rdoc_files:
216
+ extra_rdoc_files:
203
217
  - LICENSE
204
218
  - README.md
205
219
  - CHANGELOG.md
206
220
  - lib/rfm/VERSION
207
- files:
221
+ files:
208
222
  - lib/rfm/base.rb
209
223
  - lib/rfm/database.rb
210
224
  - lib/rfm/error.rb
@@ -223,7 +237,6 @@ files:
223
237
  - lib/rfm/utilities/factory.rb
224
238
  - lib/rfm/utilities/fmpxmlresult.rb
225
239
  - lib/rfm/utilities/fmresultset.rb
226
- - lib/rfm/utilities/sax_parser.rb
227
240
  - lib/rfm/utilities/xml_parser.rb
228
241
  - lib/rfm/version.rb
229
242
  - lib/rfm/xml_mini/hpricot.rb
@@ -237,41 +250,33 @@ files:
237
250
  - CHANGELOG.md
238
251
  homepage: https://rubygems.org/gems/ginjo-rfm
239
252
  licenses: []
240
-
241
253
  post_install_message:
242
- rdoc_options:
254
+ rdoc_options:
243
255
  - --line-numbers
244
256
  - --main
245
257
  - README.md
246
- require_paths:
258
+ require_paths:
247
259
  - lib
248
- required_ruby_version: !ruby/object:Gem::Requirement
260
+ required_ruby_version: !ruby/object:Gem::Requirement
249
261
  none: false
250
- requirements:
251
- - - ">="
252
- - !ruby/object:Gem::Version
253
- hash: 3
254
- segments:
262
+ requirements:
263
+ - - ! '>='
264
+ - !ruby/object:Gem::Version
265
+ version: '0'
266
+ segments:
255
267
  - 0
256
- version: "0"
257
- required_rubygems_version: !ruby/object:Gem::Requirement
268
+ hash: -510283232379124376
269
+ required_rubygems_version: !ruby/object:Gem::Requirement
258
270
  none: false
259
- requirements:
260
- - - ">"
261
- - !ruby/object:Gem::Version
262
- hash: 25
263
- segments:
264
- - 1
265
- - 3
266
- - 1
271
+ requirements:
272
+ - - ! '>'
273
+ - !ruby/object:Gem::Version
267
274
  version: 1.3.1
268
275
  requirements: []
269
-
270
276
  rubyforge_project:
271
277
  rubygems_version: 1.8.25
272
278
  signing_key:
273
279
  specification_version: 3
274
280
  summary: Ruby to Filemaker adapter
275
281
  test_files: []
276
-
277
282
  has_rdoc:
@@ -1,298 +0,0 @@
1
- # From https://github.com/ohler55/ox
2
- #gem 'ox', '1.8.5'
3
- require 'stringio'
4
- require 'ox'
5
- require 'delegate'
6
- require 'yaml'
7
- require 'rfm'
8
-
9
-
10
- ##### CORE PATCHES #####
11
-
12
- module Saxable
13
-
14
- # Default callbacks for Saxable objects.
15
- def parent; @parent || self end
16
- def start_el(name, attributes); self end
17
- def attribute(name,value); (self[name]=value) rescue nil end
18
- def end_el(value); true end
19
-
20
- def self.included(base)
21
- attr_accessor :parent, :_new_element, :_new_element_name, :_new_element_attributes
22
-
23
- class << base
24
-
25
- attr_reader :_start_el_lambdas
26
- # This is a class method that allows shortcut config, kinda like 'before_filter'.
27
- # Helper shortcut method to be used in Saxable method at class level.
28
- # When this loads, it will define a more elaborate hidden start_el operation.
29
- # Lambdas needed to allow multiple start_el calls at Saxable object class level.
30
- def start_el(el, kls)
31
- @_start_el_lambdas ||= []
32
-
33
- # Disable this to use the non-lambda version.
34
- @_start_el_lambdas << lambda do |slf|
35
- name = slf._new_element_name
36
- if (name.match(el) if el.is_a? Regexp) || name == el
37
- sub = Object.const_get(kls.to_s).new rescue kls.new
38
- sub.parent = slf
39
- sub.merge!(slf._new_element_attributes) rescue nil
40
- slf._new_element = sub
41
- yield(slf) if block_given?
42
- return slf._new_element
43
- else
44
- #return slf
45
- return nil
46
- end
47
- end
48
-
49
- # This is the actual instance-level start_el method.
50
- # The lambda version will call multiple lambdas,
51
- # whereas the single version will only call once.
52
- define_method :start_el do |name, attributes|
53
- # This is for the lambda version.
54
- self._new_element_name = name
55
- self._new_element_attributes = attributes
56
- #
57
- # This is the non-lambda one-shot only version.
58
- #
59
- # if (name.match(el) if el.is_a? Regexp) || name == el
60
- # self._new_element = Object.const_get(kls.to_s).new
61
- # self._new_element.parent = self
62
- # self._new_element_name = name
63
- # yield(self) if block_given?
64
- # return self._new_element
65
- # else
66
- # return self
67
- # #return nil
68
- # end
69
- #
70
- # This is the lambda version
71
- #
72
- begin
73
- result = (self.class.instance_variable_get(:@_start_el_lambdas).compact.each{|e| @sub = e.call(self); break if @sub}; @sub || self)
74
- #self.class.instance_variable_get(:@_start_el).last.call(self, parent, name) rescue nil || self
75
- #puts "Successfully processed lambdas"
76
- result
77
- rescue
78
- #puts "Errors processing lambdas: #{$!}"
79
- self
80
- end
81
- #
82
- end
83
- end
84
-
85
- def end_el(el)
86
- define_method :end_el do |name|
87
- if (name.match(el) if el.is_a? Regexp) || name == el
88
- self._new_element_name = name
89
- yield(self) if block_given?
90
- return true
91
- end
92
- end
93
- end
94
-
95
- def element(*args)
96
- options = args.last.is_a?(Hash) ? args.pop : {}
97
- start_el args[0].to_s, options[:class] do |slf|
98
- slf.is_a? Hash
99
- slf[slf._new_element_name] = slf._new_element
100
- elsif slf.is_a? Array
101
- slf << slf._new_element
102
- end
103
- end
104
- end
105
-
106
- end
107
- end
108
-
109
- end # Saxable
110
-
111
- class Hash
112
- include Saxable
113
- end
114
- class Array
115
- include Saxable
116
- end
117
-
118
-
119
- module SaxHandler
120
-
121
- def self.included(base)
122
- def base.build(io, initial_object)
123
- handler = new(initial_object)
124
- handler.run_parser(io)
125
- handler.cursor
126
- end
127
- end
128
-
129
- def initialize(initial_object)
130
- init_element_buffer
131
- initial_object.parent = set_cursor initial_object
132
- end
133
-
134
- def cursor
135
- @cursor
136
- end
137
-
138
- def set_cursor(obj)
139
- @cursor = obj
140
- end
141
-
142
- def init_element_buffer
143
- @element_buffer = {:name=>nil, :attr=>{}}
144
- end
145
-
146
- def send_element_buffer
147
- if element_buffer?
148
- set_cursor cursor.start_el(@element_buffer[:name], @element_buffer[:attr])
149
- init_element_buffer
150
- end
151
- end
152
-
153
- def element_buffer?
154
- @element_buffer[:name] && !@element_buffer[:name].empty?
155
- end
156
-
157
- # Add a node to an existing element.
158
- def _start_element(name, attributes=nil)
159
- send_element_buffer
160
- if attributes.nil?
161
- @element_buffer = {:name=>name, :attr=>{}}
162
- else
163
- set_cursor cursor.start_el(name, attributes)
164
- end
165
- end
166
-
167
- # Add attribute to existing element.
168
- def _attribute(name, value)
169
- @element_buffer[:attr].merge!({name=>value})
170
- #cursor.attribute(name,value)
171
- end
172
-
173
- # Add 'content' attribute to existing element.
174
- def _text(value)
175
- if !element_buffer?
176
- cursor.attribute('content', value)
177
- else
178
- @element_buffer[:attr].merge!({'content'=>value})
179
- send_element_buffer
180
- end
181
- end
182
-
183
- # Close out an existing element.
184
- def _end_element(value)
185
- send_element_buffer
186
- cursor.end_el(value) and set_cursor cursor.parent
187
- end
188
-
189
- end # SaxHandler
190
-
191
-
192
-
193
- ##### XML PARSERS - SAX HANDLERS #####
194
-
195
- class OxFmpSax < ::Ox::Sax
196
-
197
- include SaxHandler
198
-
199
- def run_parser(io)
200
- #Ox.sax_parse(self, io)
201
- File.open(io){|f| Ox.sax_parse self, f}
202
- end
203
-
204
- def start_element(name); _start_element(name.to_s.downcase); end
205
- def end_element(name); _end_element(name.to_s.downcase); end
206
- def attr(name, value); _attribute(name.to_s.downcase, value); end
207
- def text(value); _text(value); end
208
-
209
- end # OxFmpSax
210
-
211
-
212
- class FmResultset < Hash
213
- start_el 'datasource', :Datasource do |slf|
214
- slf[slf._new_element_name] = slf._new_element
215
- end
216
- start_el 'resultset', :Resultset do |slf|
217
- slf[slf._new_element_name] = slf._new_element
218
- end
219
- start_el 'metadata', :Metadata do |slf|
220
- slf[slf._new_element_name] = slf._new_element
221
- end
222
- end
223
-
224
- class Datasource < Hash
225
- start_el /.*/i, :Hash
226
- end
227
-
228
- class Metadata < Array
229
- start_el 'field-definition', :Hash do |slf|
230
- slf << slf._new_element
231
- end
232
- end
233
-
234
- class Resultset < Array
235
- start_el 'record', :Record do |slf|
236
- slf << slf._new_element
237
- end
238
- # Enable this to kill attribute parsing for this object.
239
- #def attribute(*args); end
240
- end
241
-
242
- class Record < Hash
243
- start_el 'field', :Field
244
- end
245
-
246
- class Field < Hash
247
- end_el 'field' do |slf|
248
- slf.parent[slf['name']] = slf['content']
249
- end
250
- end
251
-
252
- # This gives a generic tree structure.
253
- class Hash
254
- start_el /.*/i, :Hash do |slf|
255
- name, sub = slf._new_element_name, slf._new_element
256
-
257
- if slf[name].is_a? Array
258
- slf[name] << sub
259
- elsif slf.has_key? name
260
- tmp = slf[name]
261
- slf[name] = [tmp]
262
- slf[name] << sub
263
- else
264
- slf[name] = sub
265
- end
266
- end
267
-
268
- end_el /.*/i do |slf|
269
- parent, name = slf.parent, slf._new_element_name
270
- #parent[name]=nil if slf.empty? # Use nil for empty nodes.
271
- #parent.delete(name) if slf.empty? # Delete empty nodes.
272
- parent[name] = slf.values[0] unless slf.size > 1 or parent.size > 2 # Reduce unnecessary nodes.
273
- end
274
-
275
- end
276
-
277
-
278
- ##### DATA #####
279
-
280
- FM = 'local_testing/resultset.xml'
281
- FMP = 'local_testing/resultset_with_portals.xml'
282
- XML = 'local_testing/data_fmpxmlresult.xml'
283
- XMP = 'local_testing/data_with_portals_fmpxmlresult.xml'
284
- LAY = 'local_testing/layout.xml'
285
- XMD = 'local_testing/db_fmpxmlresult.xml'
286
- FMB = 'local_testing/resultset_with_bad_data.xml'
287
- SP = 'local_testing/SplashLayout.xml'
288
-
289
- S = StringIO.new(%{
290
- <top name="top01">
291
- <middle name="middle01" />
292
- <middle name="middle02">
293
- <bottom name="bottom01">bottom-text</bottom>
294
- </middle>
295
- <middle name="middle03">middle03-text</middle>
296
- </top>
297
- })
298
-