ginjo-rfm 2.1.7 → 3.0.0

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.
@@ -38,15 +38,21 @@ module Rfm
38
38
  class Resultset < Array
39
39
  include Config
40
40
 
41
- attr_reader :layout, :database, :server, :caller, :doc
42
- attr_reader :field_meta, :portal_meta, :include_portals, :datasource
43
- attr_reader :date_format, :time_format, :timestamp_format
44
- attr_reader :total_count, :foundset_count, :table
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
45
46
  #def_delegators :layout, :db, :database
46
- alias_method :db, :database
47
+ # alias_method :db, :database
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
+
47
50
 
48
51
  class << self
49
- alias_method :load_data, :new
52
+ def load_data(data)
53
+ Rfm::Connection
54
+ Rfm::SaxParser.parse(data, :fmresultset, Rfm::Resultset.new)
55
+ end
50
56
  end
51
57
 
52
58
  # Initializes a new ResultSet object. You will probably never do this your self (instead, use the Layout
@@ -70,122 +76,67 @@ module Rfm
70
76
  # * *portals* is a hash (with table occurrence names for keys and Field objects for values). If your
71
77
  # layout contains portals, you can find out what fields they contain here. Again, if it's the data you're
72
78
  # after, you want to look at the Record object.
73
- def initialize(*args) # xml_response, caller, portals
74
- #Was (server_obj, xml_response, layout_obj, portals=nil)
75
-
76
- options = args.rfm_extract_options!
77
- config :parent=>'caller'
78
- config sanitize_config(options, {}, true)
79
-
80
- xml_response = args[0] || options[:xml_response]
81
- doc = XmlParser.parse(xml_response, :namespace=>false, :parser=>(state[:parser] rescue nil))
82
-
83
- error = doc.error
84
- check_for_errors(error, (server.state[:raise_on_401] rescue nil))
85
-
86
- @doc = doc
87
- @caller = args[1] || options[:caller]
88
- @layout = (@caller.class.ancestors.include? Rfm::Layout::LayoutModule) ? @caller : options[:layout_object]
89
- @database = (@layout.database rescue nil) || (@caller.class == Rfm::Database ? @caller : options[:database_object])
90
- @server = (@database.server rescue nil) || (@caller.class == Rfm::Server ? @caller : options[:server_object])
91
- @field_meta ||= Rfm::CaseInsensitiveHash.new
92
- @portal_meta ||= Rfm::CaseInsensitiveHash.new
93
- @include_portals = args[2] || options[:include_portals]
94
-
95
- @datasource = doc.datasource
96
- meta = doc.meta
97
- resultset = doc.resultset
79
+ def initialize(*args) # parent, layout
80
+ config *args
81
+ self.meta
82
+ end # initialize
98
83
 
99
- @date_format = doc.date_format
100
- @time_format = doc.time_format
101
- @timestamp_format = doc.timestamp_format
102
-
103
- @foundset_count = doc.foundset_count
104
- @total_count = doc.total_count
105
- @table = doc.table
106
-
107
- (layout.table = @table) if layout and layout.table_no_load.blank?
108
-
109
- parse_fields(doc)
110
-
111
- # This will always load portal meta, even if :include_portals was not specified.
112
- # See Record for control of portal data loading.
113
- parse_portals(doc)
114
-
115
- # These were added for loading resultset from file
116
- # Kind of a hack. This should ideally condense down to just another option on the main @layout = ...
117
- # unless @layout
118
- # @layout = @datasource['layout']
119
- # @layout.instance_variable_set '@database', @datasource['database']
120
- # @layout.instance_eval do
121
- # def database
122
- # @database
123
- # end
124
- # end
125
- # end
126
-
127
- return if doc.records.blank?
128
- Rfm::Record.build_records(doc.records, self, @field_meta, @layout)
84
+ def config(*args)
85
+ super do |params|
86
+ (@layout = params[:objects][0]) if params &&
87
+ params[:objects] &&
88
+ params[:objects][0] &&
89
+ params[:objects][0].is_a?(Rfm::Layout)
90
+ end
129
91
  end
130
92
 
131
- # Load Resultset data from file-spec or string
132
- # def self.load_data(file_or_string)
133
- # self.new(file_or_string, nil)
134
- # end
135
-
136
- def state(*args)
137
- get_config(*args)
93
+ # This method was added for situations where a layout was not provided at resultset instantiation,
94
+ # such as when loading a resultset from an xml file.
95
+ def layout
96
+ @layout ||= (Layout.new(meta.layout, self) if meta.respond_to? :layout)
97
+ end
98
+
99
+ def database
100
+ layout.database
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
138
112
  end
139
-
140
- def field_names
141
- field_meta.collect{|k,v| v.name}
113
+
114
+ def handle_new_record(attributes)
115
+ r = Rfm::Record.new(self, attributes, {})
116
+ self << r
117
+ r
142
118
  end
143
119
 
144
- def portal_names
145
- portal_meta.keys
120
+ def end_datasource_element_callback(cursor)
121
+ %w(date_format time_format timestamp_format).each{|f| convert_date_time_format(eval(f))}
146
122
  end
147
123
 
148
- private
149
-
150
- def check_for_errors(code, raise_401)
151
- raise Rfm::Error.getError(code) if code != 0 && (code != 401 || raise_401)
152
- end
124
+ private
153
125
 
154
- def parse_fields(doc)
155
- return if doc.fields.blank?
156
-
157
- doc.fields.each do |field|
158
- name = layout.field_mapping[field.name] || field.name rescue field.name
159
- @field_meta[name] = Rfm::Metadata::Field.new(field)
160
- end
161
- (layout.field_names = field_names) if layout and layout.field_names_no_load.blank?
162
- end
163
-
164
- def parse_portals(doc)
165
- return if doc.portals.blank?
166
- doc.portals.each do |relatedset|
167
- next if relatedset.blank?
168
- table, fields = relatedset.table, {}
169
-
170
- relatedset.fields.each do |field|
171
- name = field.name.to_s.gsub(Regexp.new(table + '::'), '')
172
- fields[name] = Rfm::Metadata::Field.new(field)
173
- end
126
+ def check_for_errors(code=@meta['error'].to_i, raise_401=state[:raise_401])
127
+ #puts ["\nRESULTSET#check_for_errors", code, raise_401]
128
+ raise Rfm::Error.getError(code) if code != 0 && (code != 401 || raise_401)
129
+ end
174
130
 
175
- @portal_meta[table] = fields
176
- end
177
- (layout.portal_meta = @portal_meta) if layout and layout.portal_meta_no_load.blank?
178
- end
179
-
180
- # def convert_date_time_format(fm_format)
181
- # fm_format.gsub!('MM', '%m')
182
- # fm_format.gsub!('dd', '%d')
183
- # fm_format.gsub!('yyyy', '%Y')
184
- # fm_format.gsub!('HH', '%H')
185
- # fm_format.gsub!('mm', '%M')
186
- # fm_format.gsub!('ss', '%S')
187
- # fm_format
188
- # end
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
189
140
 
190
141
  end
191
142
  end
@@ -107,7 +107,6 @@ module Rfm
107
107
  # * *name* is the name of this database
108
108
  # * *state* is a hash of all server options used to initialize this server
109
109
  class Server
110
- #extend Config
111
110
  include Config
112
111
 
113
112
 
@@ -196,31 +195,8 @@ module Rfm
196
195
  # :root_cert_path => '/usr/cert_file/'
197
196
  # })
198
197
  def initialize(*args)
199
- config :parent => 'Rfm::Config'
200
- options = get_config(*args)
201
- config sanitize_config(options, {}, true)
202
- config(:host => (options[:strings].delete_at(0) || options[:host]) )
203
- raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Server has no host name. Attempted name '#{config[:host]}'.") if config[:host].to_s == ''
204
-
205
- @defaults = {
206
- :host => 'localhost',
207
- :port => 80,
208
- :ssl => true,
209
- :root_cert => true,
210
- :root_cert_name => '',
211
- :root_cert_path => '/',
212
- :account_name => '',
213
- :password => '',
214
- :log_actions => false,
215
- :log_responses => false,
216
- :log_parser => false,
217
- :warn_on_redirect => true,
218
- :raise_on_401 => false,
219
- :timeout => 60,
220
- :ignore_bad_data => false,
221
- :grammar => 'fmresultset'
222
- } #.merge(options)
223
-
198
+ config(*args)
199
+ raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Server has no host name. Attempted name '#{state[:host]}'.") if state[:host].to_s == ''
224
200
  @databases = Rfm::Factory::DbFactory.new(self)
225
201
  end
226
202
 
@@ -245,9 +221,11 @@ module Rfm
245
221
  # Legacy Rfm method to get/create databases from server object
246
222
  alias_method :db, :databases
247
223
 
248
- def state(*args)
249
- @defaults.merge(get_config(*args))
224
+ def config(*args)
225
+ super(:capture_strings_with=>[:host, :account_name, :password])
226
+ super(*args)
250
227
  end
228
+
251
229
 
252
230
  def host_name; state[:host]; end
253
231
  def scheme; state[:ssl] ? "https" : "http"; end
@@ -280,150 +258,6 @@ module Rfm
280
258
  # },
281
259
  # { :max_records => 20 }
282
260
  # )
283
- def connect(account_name, password, action, args, options = {})
284
- grammar_option = options.delete(:grammar)
285
- post = args.merge(expand_options(options)).merge({action => ''})
286
- grammar = select_grammar(post, :grammar=>grammar_option)
287
- http_fetch(host_name, port, "/fmi/xml/#{grammar}.xml", account_name, password, post)
288
- end
289
-
290
- def load_layout(layout)
291
- post = {'-db' => layout.db.name, '-lay' => layout.name, '-view' => ''}
292
- resp = http_fetch(host_name, port, "/fmi/xml/FMPXMLLAYOUT.xml", layout.db.account_name, layout.db.password, post)
293
- #remove_namespace(resp.body)
294
- end
295
-
296
- def select_grammar(post, options={})
297
- grammar = state(options)[:grammar] || 'fmresultset'
298
- if grammar.to_s.downcase == 'auto'
299
- post.keys.find(){|k| %w(-find -findall -dbnames -layoutnames -scriptnames).include? k.to_s} ? "FMPXMLRESULT" : "fmresultset"
300
- else
301
- grammar
302
- end
303
- end
304
-
305
- # Removes namespace from fmpxmllayout, so xpath will work
306
- def remove_namespace(xml)
307
- xml.gsub(/xmlns=\"[^\"]*\"/, '')
308
- end
309
-
310
- private
311
-
312
- def http_fetch(host_name, port, path, account_name, password, post_data, limit=10)
313
- raise Rfm::CommunicationError.new("While trying to reach the Web Publishing Engine, RFM was redirected too many times.") if limit == 0
314
-
315
- if state[:log_actions] == true
316
- #qs = post_data.collect{|key,val| "#{CGI::escape(key.to_s)}=#{CGI::escape(val.to_s)}"}.join("&")
317
- qs_unescaped = post_data.collect{|key,val| "#{key.to_s}=#{val.to_s}"}.join("&")
318
- #warn "#{@scheme}://#{@host_name}:#{@port}#{path}?#{qs}"
319
- warn "#{scheme}://#{host_name}:#{port}#{path}?#{qs_unescaped}"
320
- end
321
-
322
- request = Net::HTTP::Post.new(path)
323
- request.basic_auth(account_name, password)
324
- request.set_form_data(post_data)
325
-
326
- response = Net::HTTP.new(host_name, port)
327
- #ADDED LONG TIMEOUT TIMOTHY TING 05/12/2011
328
- response.open_timeout = response.read_timeout = state[:timeout]
329
- if state[:ssl]
330
- response.use_ssl = true
331
- if state[:root_cert]
332
- response.verify_mode = OpenSSL::SSL::VERIFY_PEER
333
- response.ca_file = File.join(state[:root_cert_path], state[:root_cert_name])
334
- else
335
- response.verify_mode = OpenSSL::SSL::VERIFY_NONE
336
- end
337
- end
338
-
339
- response = response.start { |http| http.request(request) }
340
- if state[:log_responses] == true
341
- response.to_hash.each { |key, value| warn "#{key}: #{value}" }
342
- warn response.body
343
- end
344
-
345
- case response
346
- when Net::HTTPSuccess
347
- response
348
- when Net::HTTPRedirection
349
- if state[:warn_on_redirect]
350
- warn "The web server redirected to " + response['location'] +
351
- ". You should revise your connection hostname or fix your server configuration if possible to improve performance."
352
- end
353
- newloc = URI.parse(response['location'])
354
- http_fetch(newloc.host, newloc.port, newloc.request_uri, account_name, password, post_data, limit - 1)
355
- when Net::HTTPUnauthorized
356
- msg = "The account name (#{account_name}) or password provided is not correct (or the account doesn't have the fmxml extended privilege)."
357
- raise Rfm::AuthenticationError.new(msg)
358
- when Net::HTTPNotFound
359
- msg = "Could not talk to FileMaker because the Web Publishing Engine is not responding (server returned 404)."
360
- raise Rfm::CommunicationError.new(msg)
361
- else
362
- msg = "Unexpected response from server: #{response.code} (#{response.class.to_s}). Unable to communicate with the Web Publishing Engine."
363
- raise Rfm::CommunicationError.new(msg)
364
- end
365
- end
366
-
367
- def expand_options(options)
368
- result = {}
369
- field_mapping = options.delete(:field_mapping) || {}
370
- #puts field_mapping.to_yaml
371
- options.each do |key,value|
372
- case key.to_sym
373
- when :max_portal_rows
374
- result['-relatedsets.max'] = value
375
- result['-relatedsets.filter'] = 'layout'
376
- when :max_records
377
- result['-max'] = value
378
- when :skip_records
379
- result['-skip'] = value
380
- when :sort_field
381
- if value.kind_of? Array
382
- raise Rfm::ParameterError.new(":sort_field can have at most 9 fields, but you passed an array with #{value.size} elements.") if value.size > 9
383
- value.each_index { |i| result["-sortfield.#{i+1}"] = field_mapping[value[i]] || value[i] }
384
- else
385
- result["-sortfield.1"] = field_mapping[value] || value
386
- end
387
- when :sort_order
388
- if value.kind_of? Array
389
- raise Rfm::ParameterError.new(":sort_order can have at most 9 fields, but you passed an array with #{value.size} elements.") if value.size > 9
390
- value.each_index { |i| result["-sortorder.#{i+1}"] = value[i] }
391
- else
392
- result["-sortorder.1"] = value
393
- end
394
- when :post_script
395
- if value.class == Array
396
- result['-script'] = value[0]
397
- result['-script.param'] = value[1]
398
- else
399
- result['-script'] = value
400
- end
401
- when :pre_find_script
402
- if value.class == Array
403
- result['-script.prefind'] = value[0]
404
- result['-script.prefind.param'] = value[1]
405
- else
406
- result['-script.presort'] = value
407
- end
408
- when :pre_sort_script
409
- if value.class == Array
410
- result['-script.presort'] = value[0]
411
- result['-script.presort.param'] = value[1]
412
- else
413
- result['-script.presort'] = value
414
- end
415
- when :response_layout
416
- result['-lay.response'] = value
417
- when :logical_operator
418
- result['-lop'] = value
419
- when :modification_id
420
- result['-modid'] = value
421
- else
422
- raise Rfm::ParameterError.new("Invalid option: #{key}")
423
- end
424
- end
425
- return result
426
- end
427
261
 
428
262
  end
429
263
  end
@@ -1,7 +1,14 @@
1
1
  module Rfm
2
-
3
- # Should these go in Rfm module?
4
- CONFIG_KEYS = %w(
2
+ #
3
+ # Top level config hash accepts any defined config parameters,
4
+ # or group-name keys pointing to config subsets.
5
+ # The subsets can be any grouping of defined config parameters, as a hash.
6
+ # See CONFIG_KEYS for defined config parameters.
7
+ #
8
+ module Config
9
+ require 'yaml'
10
+
11
+ CONFIG_KEYS = %w(
5
12
  file_name
6
13
  file_path
7
14
  parser
@@ -26,17 +33,11 @@ module Rfm
26
33
  parent
27
34
  grammar
28
35
  field_mapping
36
+ capture_strings_with
37
+ logger
29
38
  )
30
39
 
31
- CONFIG_DONT_STORE = %w(strings using parents symbols objects)
32
-
33
- # Top level config hash accepts any defined config parameters,
34
- # or group-name keys pointing to config subsets.
35
- # The subsets can be any grouping of defined config parameters, as a hash.
36
- # See CONFIG_KEYS for defined config parameters.
37
- #
38
- module Config
39
- require 'yaml'
40
+ CONFIG_DONT_STORE = %w(strings using parents symbols objects) #capture_strings_with)
40
41
 
41
42
  extend self
42
43
  @config = {}
@@ -57,17 +58,17 @@ module Rfm
57
58
  # Factory.layout 'my_layout', :my_group # to get a layout from settings in :my_group
58
59
  #
59
60
  def config(*args, &block)
60
- opt = args.rfm_extract_options!
61
61
  @config ||= {}
62
- config_write(opt, args, &block)
62
+ return @config if args.empty?
63
+ config_write(*args, &block)
63
64
  @config
64
65
  end
65
66
 
66
67
  # Sets @config just as above config method, but clears @config first.
67
68
  def config_clear(*args)
68
- opt = args.rfm_extract_options!
69
69
  @config = {}
70
- config_write(opt, args)
70
+ return @config if args.empty?
71
+ config_write(*args)
71
72
  @config
72
73
  end
73
74
 
@@ -84,21 +85,15 @@ module Rfm
84
85
  # get_config :layout => 'my_layout
85
86
  #
86
87
  def get_config(*arguments)
88
+ #puts caller_locations(1,1)[0]
87
89
  args = arguments.clone
88
90
  @config ||= {}
89
- opt = args.rfm_extract_options!
90
- strings = opt[:strings].rfm_force_array || []
91
- symbols = opt[:use].rfm_force_array || []
92
- objects = opt[:objects].rfm_force_array || []
93
- args.each do |arg|
94
- case true
95
- when arg.is_a?(String) ; strings << arg
96
- when arg.is_a?(Symbol) ; symbols << arg
97
- else objects.unshift arg
98
- end
99
- end
91
+ options = config_extract_options!(*args)
92
+ strings = options[:strings].rfm_force_array || []
93
+ symbols = options[:symbols].rfm_force_array.concat(options[:hash][:use].rfm_force_array) || []
94
+ objects = options[:objects].rfm_force_array || []
100
95
 
101
- rslt = config_merge_with_parent(symbols).merge(opt)
96
+ rslt = config_merge_with_parent(symbols).merge(options[:hash])
102
97
  #using = rslt[:using].rfm_force_array
103
98
  sanitize_config(rslt, CONFIG_DONT_STORE, false)
104
99
  rslt[:using].delete ""
@@ -106,12 +101,16 @@ module Rfm
106
101
  rslt.merge(:strings=>strings, :objects=>objects)
107
102
  end
108
103
 
109
- # Keep should be a list of strings representing keys to keep.
110
- def sanitize_config(conf={}, keep=[], dupe=false)
111
- (conf = conf.clone) if dupe
112
- conf.reject!{|k,v| (!CONFIG_KEYS.include?(k.to_s) or [{},[],''].include?(v)) and !keep.include? k.to_s }
113
- conf
114
- end
104
+ def state(*args)
105
+ return @_state if args.empty? && !@state.nil? && (RUBY_VERSION[0,1].to_i > 1 ? (caller_locations(1,1) == @_last_state_caller) : false)
106
+ @_state = get_config(*args)
107
+ (@_last_state_caller = caller_locations(1,1)) if RUBY_VERSION[0,1].to_i > 1
108
+ @_state
109
+ end
110
+
111
+ def log
112
+ Rfm.log
113
+ end
115
114
 
116
115
  protected
117
116
 
@@ -130,38 +129,66 @@ module Rfm
130
129
  end.inject({}){|h,a| h.merge(a)}
131
130
  ) || {}
132
131
  end
133
-
132
+
133
+ # Get the top-level configuration from yml file and RFM_CONFIG
134
+ def get_config_base
135
+ get_config_file.merge((defined?(RFM_CONFIG) and RFM_CONFIG.is_a?(Hash)) ? RFM_CONFIG : {})
136
+ end
137
+
138
+ # Get the parent configuration object according to @config[:parent]
139
+ def get_config_parent
140
+ @config ||= {}
141
+ parent = case
142
+ when @config[:parent].is_a?(String); eval(@config[:parent])
143
+ when !@config[:parent].nil? && @config[:parent].is_a?(Rfm::Config); @config[:parent]
144
+ else eval('Rfm::Config')
145
+ end
146
+ end
134
147
 
135
148
  # Merge args into @config, as :use=>[arg1, arg2, ...]
136
149
  # Then merge optional config hash into @config.
137
- # Pass in a block to use with strings in args. See base.rb.
138
- def config_write(opt, args)
139
- strings = []; while args[0].is_a?(String) do; strings << args.shift; end
140
- args.each{|a| @config.merge!(:use=>a.to_sym)}
141
- @config.merge!(opt).reject! {|k,v| CONFIG_DONT_STORE.include? k.to_s}
142
- yield(strings) if block_given?
150
+ # Pass in a block to use with parsed config in args.
151
+ def config_write(*args) #(opt, args)
152
+ options = config_extract_options!(*args)
153
+ options[:symbols].each{|a| @config.merge!(:use=>a.to_sym){|h,v1,v2| [v1].flatten << v2 }}
154
+ @config.merge!(options[:hash]).reject! {|k,v| CONFIG_DONT_STORE.include? k.to_s}
155
+ #options[:hash][:capture_strings_with].rfm_force_array.each do |label|
156
+ @config[:capture_strings_with].rfm_force_array.each do |label|
157
+ string = options[:strings].delete_at(0)
158
+ (@config[label] = string) if string && !string.empty?
159
+ end
160
+ parent = (options[:objects].delete_at(0) || options[:hash][:parent])
161
+ (@config[:parent] = parent) if parent
162
+ yield(options) if block_given?
143
163
  end
144
164
 
145
165
  # Get composite config from all levels, processing :use parameters at each level
146
166
  def config_merge_with_parent(filters=nil)
147
- remote = if (self != Rfm::Config)
148
- eval(@config[:parent] || 'Rfm::Config').config_merge_with_parent rescue {}
167
+ @config ||= {}
168
+
169
+ # Get upstream compilation
170
+ upstream = if (self != Rfm::Config)
171
+ #puts [self, @config[:parent], get_config_parent].join(', ')
172
+ get_config_parent.config_merge_with_parent
149
173
  else
150
- get_config_file.merge((defined?(RFM_CONFIG) and RFM_CONFIG.is_a?(Hash)) ? RFM_CONFIG : {})
174
+ get_config_base
151
175
  end.clone
152
-
153
- remote[:using] ||= []
154
- remote[:parents] ||= ['file', 'RFM_CONFIG']
176
+
177
+ upstream[:using] ||= []
178
+ upstream[:parents] ||= ['file', 'RFM_CONFIG']
155
179
 
156
180
  filters = (@config[:use].rfm_force_array | filters.rfm_force_array).compact
157
- rslt = config_filter(remote, filters).merge(config_filter(@config, filters))
158
-
181
+
182
+ rslt = config_filter(upstream, filters).merge(config_filter(@config, filters))
183
+
159
184
  rslt[:using].concat((@config[:use].rfm_force_array | filters).compact.flatten) #.join
160
185
  rslt[:parents] << @config[:parent].to_s
161
186
 
162
187
  rslt.delete :parent
163
188
 
164
- rslt
189
+ rslt || {}
190
+ # rescue
191
+ # puts "Config#config_merge_with_parent for '#{self.class}' falied with #{$1}"
165
192
  end
166
193
 
167
194
  # Returns a configuration hash overwritten by :use filters in the hash
@@ -169,15 +196,33 @@ module Rfm
169
196
  def config_filter(conf, filters=nil)
170
197
  conf = conf.clone
171
198
  filters = (conf[:use].rfm_force_array | filters.rfm_force_array).compact
172
- filters.each{|f| next unless conf[f]; conf.merge!(conf[f] || {})} if !filters.blank?
199
+ filters.each{|f| next unless conf[f]; conf.merge!(conf[f] || {})} if (!filters.nil? && !filters.empty?)
173
200
  conf.delete(:use)
174
201
  conf
175
202
  end
176
203
 
177
-
178
- # This loads RFM_CONFIG into @config. It is not necessary,
179
- # as get_config will merge all configuration into RFM_CONFIG at runtime.
180
- #config RFM_CONFIG if defined? RFM_CONFIG
204
+ # Extract arguments into strings, symbols, objects, hash.
205
+ def config_extract_options!(*args)
206
+ strings, symbols, objects = [], [], []
207
+ options = args.last.is_a?(Hash) ? args.pop : {}
208
+ args.each do |a|
209
+ case
210
+ when a.is_a?(String); strings << a
211
+ when a.is_a?(Symbol); symbols << a
212
+ else objects << a
213
+ end
214
+ end
215
+ {:strings=>strings, :symbols=>symbols, :objects=>objects, :hash=>options}
216
+ end
217
+
218
+ # Remove un-registered keys from a configuration hash.
219
+ # Keep should be a list of strings representing keys to keep.
220
+ def sanitize_config(conf={}, keep=[], dupe=false)
221
+ (conf = conf.clone) if dupe
222
+ conf.reject!{|k,v| (!CONFIG_KEYS.include?(k.to_s) or [{},[],''].include?(v)) and !keep.include? k.to_s }
223
+ conf
224
+ end
225
+
181
226
 
182
227
  end # module Config
183
228