ginjo-rfm 1.4.4 → 2.0.pre31
Sign up to get free protection for your applications and to get access to all the features.
- 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
|