ginjo-rfm 1.4.4 → 2.0.pre31
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.
- data/CHANGELOG.md +107 -0
- data/README.md +378 -133
- data/lib/rfm.rb +51 -19
- data/lib/rfm/VERSION +1 -1
- data/lib/rfm/base.rb +416 -0
- data/lib/rfm/database.rb +14 -9
- data/lib/rfm/layout.rb +148 -96
- data/lib/rfm/metadata/field.rb +5 -5
- data/lib/rfm/metadata/field_control.rb +52 -51
- data/lib/rfm/metadata/script.rb +7 -5
- data/lib/rfm/record.rb +71 -56
- data/lib/rfm/resultset.rb +45 -26
- data/lib/rfm/server.rb +21 -17
- data/lib/rfm/utilities/complex_query.rb +64 -0
- data/lib/rfm/utilities/config.rb +115 -0
- data/lib/rfm/utilities/core_ext.rb +90 -0
- data/lib/rfm/utilities/factory.rb +100 -17
- data/lib/rfm/utilities/xml_parser.rb +94 -0
- data/lib/rfm/version.rb +1 -1
- data/lib/rfm/xml_mini/hpricot.rb +133 -0
- metadata +87 -30
@@ -0,0 +1,115 @@
|
|
1
|
+
module Rfm
|
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
|
+
# All filters are honored, unless filters are included in calls to get_config,
|
9
|
+
# in which case only the immediately specified filters will be used.
|
10
|
+
#
|
11
|
+
# Do not put a :use=>:group filter in a subset (maybe future feature?).
|
12
|
+
# Do not put a :use=>:group filter in the top-level global parameters.
|
13
|
+
# Do not put subsets in non-top-level configs. (maybe future feature?)
|
14
|
+
#
|
15
|
+
module Config
|
16
|
+
CONFIG_KEYS = %w(parser host port account_name password database layout ssl root_cert root_cert_name root_cert_path warn_on_redirect raise_on_401 timeout log_actions log_responses log_parser use parent)
|
17
|
+
|
18
|
+
extend self
|
19
|
+
@config = {}
|
20
|
+
|
21
|
+
# Set @config with args & options hash.
|
22
|
+
# Args should be symbols representing configuration groups,
|
23
|
+
# with optional config hash as last arg, to be merged on top.
|
24
|
+
# Returns @config.
|
25
|
+
#
|
26
|
+
# == Sets @config with :use => :group1, :layout => 'my_layout'
|
27
|
+
# config :group1, :layout => 'my_layout
|
28
|
+
#
|
29
|
+
# Factory.server, Factory.database, Factory.layout, and Base.config can take
|
30
|
+
# a string as the first argument, refering to the relevent server/database/layout name.
|
31
|
+
#
|
32
|
+
# == Pass a string as the first argument, to be used in the immediate context
|
33
|
+
# config 'my_layout' # in the model, to set model configuration
|
34
|
+
# Factory.layout 'my_layout', :my_group # to get a layout from settings in :my_group
|
35
|
+
#
|
36
|
+
def config(*args, &block)
|
37
|
+
opt = args.rfm_extract_options!
|
38
|
+
@config ||= {}
|
39
|
+
config_write(opt, args, &block)
|
40
|
+
@config
|
41
|
+
end
|
42
|
+
|
43
|
+
# Sets @config just as above config method, but clears @config first.
|
44
|
+
def config_clear(*args)
|
45
|
+
opt = args.rfm_extract_options!
|
46
|
+
@config = {}
|
47
|
+
config_write(opt, args)
|
48
|
+
@config
|
49
|
+
end
|
50
|
+
|
51
|
+
# Reads compiled config, including filters and ad-hoc configuration params passed in.
|
52
|
+
# If first n parameters are strings, they will be appended to config[:strings].
|
53
|
+
# If next n parameters are symbols, they will be used to filter the result. These
|
54
|
+
# filters will override all stored config[:use] settings.
|
55
|
+
# The final optional hash should be ad-hoc config settings.
|
56
|
+
#
|
57
|
+
# == Gets top level settings, merged with group settings, merged with local and ad-hoc settings.
|
58
|
+
# get_config :my_server_group, :layout => 'my_layout' # This gets top level settings,
|
59
|
+
#
|
60
|
+
# == Gets top level settings, merged with local and ad-hoc settings.
|
61
|
+
# get_config :layout => 'my_layout
|
62
|
+
#
|
63
|
+
def get_config(*args)
|
64
|
+
@config ||= {}
|
65
|
+
opt = args.rfm_extract_options!
|
66
|
+
strings = []
|
67
|
+
while args[0].is_a?(String) do; strings << args.shift; end
|
68
|
+
if args.size == 0
|
69
|
+
config_filter(config_merge_with_parent)
|
70
|
+
else
|
71
|
+
config_filter(config_merge_with_parent, args)
|
72
|
+
end.merge(opt).merge(:strings=>strings)
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
|
78
|
+
# Merge args into @config, as :use=>[arg1, arg2, ...]
|
79
|
+
# Then merge optional config hash into @config.
|
80
|
+
# Pass in a block to use with strings in args. See base.rb.
|
81
|
+
def config_write(opt, args)
|
82
|
+
strings = []; while args[0].is_a?(String) do; strings << args.shift; end
|
83
|
+
args.each{|a| @config.merge!(:use=>a.to_sym)}
|
84
|
+
@config.merge!(opt)
|
85
|
+
yield(strings) if block_given?
|
86
|
+
end
|
87
|
+
|
88
|
+
# Get composite config from all levels, adding :use parameters to a
|
89
|
+
# temporary top-level value.
|
90
|
+
def config_merge_with_parent
|
91
|
+
remote = if (self != Rfm::Config)
|
92
|
+
eval(@config[:parent] || 'Rfm::Config').config_merge_with_parent rescue {}
|
93
|
+
else
|
94
|
+
(defined?(RFM_CONFIG) and RFM_CONFIG.is_a?(Hash)) ? RFM_CONFIG : {}
|
95
|
+
end
|
96
|
+
|
97
|
+
use = (remote[:use].rfm_force_array | @config[:use].rfm_force_array).compact
|
98
|
+
remote.merge(@config).merge(:use=>use)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Given config hash, return filtered. Filters should be symbols.
|
102
|
+
def config_filter(conf, filters=nil)
|
103
|
+
filters ||= conf[:use].rfm_force_array if !conf[:use].blank?
|
104
|
+
filters.each{|f| next unless conf[f]; conf.merge!(conf[f] || {})} if !filters.blank?
|
105
|
+
conf.reject!{|k,v| !CONFIG_KEYS.include?(k.to_s) or v.to_s == '' }
|
106
|
+
conf
|
107
|
+
end
|
108
|
+
|
109
|
+
# This loads RFM_CONFIG into @config. It is not necessary,
|
110
|
+
# as get_config will merge all configuration into RFM_CONFIG at runtime.
|
111
|
+
#config RFM_CONFIG if defined? RFM_CONFIG
|
112
|
+
|
113
|
+
end # module Config
|
114
|
+
|
115
|
+
end # module Rfm
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
Module.module_eval do
|
4
|
+
# Adds ability to forward methods to other objects using 'def_delegator'
|
5
|
+
include Forwardable
|
6
|
+
end
|
7
|
+
|
8
|
+
class Object # @private :nodoc: all
|
9
|
+
|
10
|
+
#extend Forwardable
|
11
|
+
|
12
|
+
# Adds methods to put instance variables in rfm_metaclass, plus getter/setters
|
13
|
+
# This is useful to hide instance variables in objects that would otherwise show "too much" information.
|
14
|
+
def self.meta_attr_accessor(*names)
|
15
|
+
meta_attr_reader(*names)
|
16
|
+
meta_attr_writer(*names)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.meta_attr_reader(*names)
|
20
|
+
names.each do |n|
|
21
|
+
define_method(n.to_s) {rfm_metaclass.instance_variable_get("@#{n}")}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.meta_attr_writer(*names)
|
26
|
+
names.each do |n|
|
27
|
+
define_method(n.to_s + "=") {|val| rfm_metaclass.instance_variable_set("@#{n}", val)}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Wrap an object in Array, if not already an Array,
|
32
|
+
# since XmlMini doesn't know which will be returnd for any particular element.
|
33
|
+
# See Rfm Layout & Record where this is used.
|
34
|
+
def rfm_force_array
|
35
|
+
self.is_a?(Array) ? self : [self]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Just testing this functionality
|
39
|
+
def local_methods
|
40
|
+
self.methods - self.class.superclass.methods
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Like singleton_method or 'metaclass' from ActiveSupport.
|
46
|
+
def rfm_metaclass
|
47
|
+
class << self
|
48
|
+
self
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get the superclass object of self.
|
53
|
+
def rfm_super
|
54
|
+
SuperProxy.new(self)
|
55
|
+
end
|
56
|
+
|
57
|
+
end # Object
|
58
|
+
|
59
|
+
|
60
|
+
class Array
|
61
|
+
# Taken from ActiveSupport extract_options!.
|
62
|
+
def rfm_extract_options!
|
63
|
+
last.is_a?(::Hash) ? pop : {}
|
64
|
+
end
|
65
|
+
end # Array
|
66
|
+
|
67
|
+
# Allows access to superclass object
|
68
|
+
class SuperProxy
|
69
|
+
def initialize(obj)
|
70
|
+
@obj = obj
|
71
|
+
end
|
72
|
+
|
73
|
+
def method_missing(meth, *args, &blk)
|
74
|
+
@obj.class.superclass.instance_method(meth).bind(@obj).call(*args, &blk)
|
75
|
+
end
|
76
|
+
end # SuperProxy
|
77
|
+
|
78
|
+
|
79
|
+
class Time
|
80
|
+
# Returns array of [date,time] in format suitable for FMP.
|
81
|
+
def to_fm_components(reset_time_if_before_today=false)
|
82
|
+
d = self.strftime('%m/%d/%Y')
|
83
|
+
t = if (Date.parse(self.to_s) < Date.today) and reset_time_if_before_today==true
|
84
|
+
"00:00:00"
|
85
|
+
else
|
86
|
+
self.strftime('%T')
|
87
|
+
end
|
88
|
+
[d,t]
|
89
|
+
end
|
90
|
+
end # Time
|
@@ -7,32 +7,64 @@
|
|
7
7
|
|
8
8
|
|
9
9
|
module Rfm
|
10
|
-
|
11
|
-
|
10
|
+
|
11
|
+
module Factory
|
12
|
+
extend Config
|
13
|
+
config :parent=>'Rfm::Config'
|
14
|
+
|
15
|
+
class ServerFactory < Rfm::CaseInsensitiveHash # @private :nodoc: all
|
16
|
+
|
17
|
+
def [](host, conf = Factory.get_config) #(Factory.instance_variable_get(:@config) || {}))
|
18
|
+
conf[:host] = host
|
19
|
+
super(host) or (self[host] = Rfm::Server.new(conf.reject{|k,v| [:account_name, :password].include? k}))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Potential refactor
|
23
|
+
# def [](*conf)
|
24
|
+
# options = Factory.get_config(*conf)
|
25
|
+
# server_name = options[:strings][0] || options[:host]
|
26
|
+
# options[:host] = server_name
|
27
|
+
# #server = servers[server_name, options]
|
28
|
+
# super(server_name) or (self[server_name] = Rfm::Server.new(options.reject{|k,v| [:account_name, :password].include? k}))
|
29
|
+
# end
|
30
|
+
|
31
|
+
end # ServerFactory
|
32
|
+
|
33
|
+
|
34
|
+
class DbFactory < Rfm::CaseInsensitiveHash # :nodoc: all
|
12
35
|
|
13
36
|
def initialize(server)
|
14
37
|
@server = server
|
15
38
|
@loaded = false
|
16
39
|
end
|
17
40
|
|
18
|
-
def [](dbname)
|
19
|
-
super or (self[dbname] = Rfm::Database.new(dbname, @server))
|
41
|
+
def [](dbname, acnt=nil, pass=nil) #
|
42
|
+
db = (super(dbname) or (self[dbname] = Rfm::Database.new(dbname, @server)))
|
43
|
+
account_name = acnt || db.account_name || @server.state[:account_name]
|
44
|
+
password = pass || db.password || @server.state[:password]
|
45
|
+
db.account_name = account_name if account_name
|
46
|
+
db.password = password if password
|
47
|
+
db
|
20
48
|
end
|
21
49
|
|
22
50
|
def all
|
23
51
|
if !@loaded
|
24
52
|
Rfm::Resultset.new(@server, @server.connect(@server.state[:account_name], @server.state[:password], '-dbnames', {}).body, nil).each {|record|
|
25
53
|
name = record['DATABASE_NAME']
|
26
|
-
self[name] = Rfm::Database.new(name, @server) if self
|
54
|
+
self[name] = Rfm::Database.new(name, @server) if self.keys.find{|k| k.to_s.downcase == name.to_s.downcase} == nil
|
27
55
|
}
|
28
56
|
@loaded = true
|
29
57
|
end
|
30
|
-
self
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def names
|
62
|
+
keys
|
31
63
|
end
|
32
64
|
|
33
|
-
end
|
65
|
+
end # DbFactory
|
34
66
|
|
35
|
-
class LayoutFactory < Rfm::CaseInsensitiveHash
|
67
|
+
class LayoutFactory < Rfm::CaseInsensitiveHash # :nodoc: all
|
36
68
|
|
37
69
|
def initialize(server, database)
|
38
70
|
@server = server
|
@@ -46,18 +78,24 @@ module Rfm
|
|
46
78
|
|
47
79
|
def all
|
48
80
|
if !@loaded
|
49
|
-
Rfm::Resultset.new(@server, @server.connect(@
|
81
|
+
Rfm::Resultset.new(@server, @server.connect(@database.account_name, @database.password, '-layoutnames', {"-db" => @database.name}).body, nil).each {|record|
|
50
82
|
name = record['LAYOUT_NAME']
|
51
83
|
self[name] = Rfm::Layout.new(name, @database) if self[name] == nil
|
52
84
|
}
|
53
85
|
@loaded = true
|
54
86
|
end
|
55
|
-
self
|
87
|
+
self
|
56
88
|
end
|
57
89
|
|
58
|
-
|
90
|
+
def names
|
91
|
+
keys
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
end # LayoutFactory
|
59
97
|
|
60
|
-
class ScriptFactory < Rfm::CaseInsensitiveHash
|
98
|
+
class ScriptFactory < Rfm::CaseInsensitiveHash # :nodoc: all
|
61
99
|
|
62
100
|
def initialize(server, database)
|
63
101
|
@server = server
|
@@ -71,15 +109,60 @@ module Rfm
|
|
71
109
|
|
72
110
|
def all
|
73
111
|
if !@loaded
|
74
|
-
Rfm::Resultset.new(@server, @server.connect(@
|
112
|
+
Rfm::Resultset.new(@server, @server.connect(@database.account_name, @database.password, '-scriptnames', {"-db" => @database.name}).body, nil).each {|record|
|
75
113
|
name = record['SCRIPT_NAME']
|
76
114
|
self[name] = Rfm::Metadata::Script.new(name, @database) if self[name] == nil
|
77
115
|
}
|
78
116
|
@loaded = true
|
79
117
|
end
|
80
|
-
self
|
118
|
+
self
|
81
119
|
end
|
120
|
+
|
121
|
+
def names
|
122
|
+
keys
|
123
|
+
end
|
124
|
+
|
125
|
+
end # ScriptFactory
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
class << self
|
130
|
+
|
131
|
+
def servers
|
132
|
+
@servers ||= ServerFactory.new
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns Rfm::Server instance, given config hash or array
|
136
|
+
def server(*conf)
|
137
|
+
options = get_config(*conf)
|
138
|
+
server_name = options[:strings][0] || options[:host]
|
139
|
+
raise Rfm::Error::RfmError.new(0, 'A host name is needed to create a server object.') if server_name.blank?
|
140
|
+
server = servers[server_name, options]
|
141
|
+
end
|
142
|
+
# Potential refactor
|
143
|
+
#def_delegator 'Rfm::Factory::ServerFactory', :[], :server #, :[]
|
144
|
+
|
145
|
+
# Returns Rfm::Db instance, given config hash or array
|
146
|
+
def db(*conf)
|
147
|
+
options = get_config(*conf)
|
148
|
+
db_name = options[:strings][0] || options[:database]
|
149
|
+
raise Rfm::Error::RfmError.new(0, 'A database name is needed to create a database object.') if db_name.blank?
|
150
|
+
account_name = options[:strings][1] || options[:account_name]
|
151
|
+
password = options[:strings][2] || options[:password]
|
152
|
+
db = server(options)[db_name, account_name, password]
|
153
|
+
end
|
154
|
+
|
155
|
+
alias_method :database, :db
|
156
|
+
|
157
|
+
# Returns Rfm::Layout instance, given config hash or array
|
158
|
+
def layout(*conf)
|
159
|
+
options = get_config(*conf)
|
160
|
+
layout_name = options[:strings][0] || options[:layout]
|
161
|
+
raise Rfm::Error::RfmError.new(0, 'A layout name is needed to create a layout object.') if layout_name.blank?
|
162
|
+
layout = db(options)[layout_name]
|
163
|
+
end
|
164
|
+
|
165
|
+
end # class << self
|
82
166
|
|
83
|
-
|
84
|
-
|
85
|
-
end
|
167
|
+
end # Factory
|
168
|
+
end # Rfm
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Rfm
|
2
|
+
module XmlParser # @private :nodoc: all
|
3
|
+
|
4
|
+
extend Config
|
5
|
+
config :parent=>'Rfm::Config'
|
6
|
+
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# Backend configurations
|
10
|
+
BACKENDS = ActiveSupport::OrderedHash.new
|
11
|
+
|
12
|
+
BACKENDS[:jdom] = {:require=>'jdom', :class => 'JDOM'}
|
13
|
+
BACKENDS[:libxml] = {:require=>'libxml', :class => 'LibXML'}
|
14
|
+
BACKENDS[:libxmlsax] = {:require=>'libxml', :class => 'LibXMLSAX'}
|
15
|
+
BACKENDS[:nokogirisax] = {:require=>'nokogiri', :class => proc{ActiveSupport::VERSION; 'NokogiriSAX'}}
|
16
|
+
BACKENDS[:nokogiri] = {:require=>'nokogiri', :class => 'Nokogiri'}
|
17
|
+
BACKENDS[:hpricot] = {:require=>'hpricot', :class => proc{
|
18
|
+
# Hpricot module is part of Rfm, not XmlMini,
|
19
|
+
# and needs to be handed manually to XmlMini.
|
20
|
+
require File.join(File.dirname(__FILE__), '../xml_mini/hpricot.rb')
|
21
|
+
ActiveSupport::XmlMini_Hpricot}}
|
22
|
+
BACKENDS[:rexml] = {:require=>'rexml/document', :class=>'REXML'}
|
23
|
+
|
24
|
+
|
25
|
+
# Main parsing method.
|
26
|
+
# Options
|
27
|
+
# :namespace => false # strip out namespace (default)
|
28
|
+
# :parser => :libxml, :libxmlsax, :nokogirisax, :nokogiri, :hpricot, :rexml
|
29
|
+
# :parser => CustomParsingModule # see ActiveSupport::XmlMini
|
30
|
+
def new(string_or_file, opts={})
|
31
|
+
string_or_file.gsub!(/xmlns=\"[^\"]*\"/,'') if (string_or_file.class == String and opts[:namespace] == false)
|
32
|
+
unless opts[:parser] and get_backend_from_hash(opts[:parser]).to_s != self.backend.to_s
|
33
|
+
warn "XmlParser default: #{ActiveSupport::XmlMini.backend.to_s}" if get_config[:log_parser] == true
|
34
|
+
ActiveSupport::XmlMini.parse(string_or_file)
|
35
|
+
else
|
36
|
+
warn "XmlParser backend: #{get_backend_from_hash(opts[:parser]).to_s}" if get_config[:log_parser] == true
|
37
|
+
ActiveSupport::XmlMini.with_backend(get_backend_from_hash(opts[:parser])) {ActiveSupport::XmlMini.parse(string_or_file)}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Shortcut to XmlMini config getter.
|
42
|
+
# If a parameter is passed, it will be used to get the local backend description
|
43
|
+
# from the BACKENDS hash, instead of the XmlMini backend.
|
44
|
+
def backend(name=nil)
|
45
|
+
return ActiveSupport::XmlMini.backend unless name
|
46
|
+
get_backend_from_hash(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Shortcut to XmlMini config setter.
|
50
|
+
def backend=(name)
|
51
|
+
if name.is_a? Symbol
|
52
|
+
set_backend_via_hash(name)
|
53
|
+
else
|
54
|
+
ActiveSupport::XmlMini.backend = name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Given name, return backend config from BACKENDS, including any preloading.
|
61
|
+
# Will raise LoadError if can't load backend.
|
62
|
+
def get_backend_from_hash(name)
|
63
|
+
backend_hash = BACKENDS[name.to_sym]
|
64
|
+
require backend_hash[:require]
|
65
|
+
backend_hash[:class].is_a?(Proc) ? backend_hash[:class].call : backend_hash[:class]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Set XmlMini backend, given symbol matching one of the BACKENDS.
|
69
|
+
def set_backend_via_hash(name)
|
70
|
+
ActiveSupport::XmlMini.backend = get_backend_from_hash(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Select the best backend from BACKENDS, returns backend config.
|
74
|
+
def decide_backend
|
75
|
+
string_or_class = catch(:done) do
|
76
|
+
BACKENDS.keys.each do |name|
|
77
|
+
begin
|
78
|
+
result = get_backend_from_hash name
|
79
|
+
throw(:done, result)
|
80
|
+
rescue LoadError, StandardError
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Set XmlMini backend when this file loads.
|
87
|
+
begin
|
88
|
+
self.backend = get_backend_from_hash(get_config[:parser])
|
89
|
+
rescue
|
90
|
+
self.backend = decide_backend
|
91
|
+
end
|
92
|
+
|
93
|
+
end # XmlParser
|
94
|
+
end # Rfm
|