og 0.27.0 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ProjectInfo +2 -2
- data/README +8 -4
- data/Rakefile +1 -1
- data/doc/AUTHORS +1 -1
- data/doc/RELEASES +81 -0
- data/examples/README +7 -0
- data/lib/glue/cacheable.rb +152 -0
- data/lib/glue/hierarchical.rb +5 -4
- data/lib/glue/optimistic_locking.rb +0 -1
- data/lib/glue/orderable.rb +46 -44
- data/lib/glue/taggable.rb +7 -4
- data/lib/glue/timestamped.rb +1 -1
- data/lib/og.rb +13 -6
- data/lib/og/entity.rb +226 -9
- data/lib/og/evolution.rb +2 -2
- data/lib/og/ez/clause.rb +147 -0
- data/lib/og/ez/condition.rb +181 -0
- data/lib/og/manager.rb +31 -30
- data/lib/og/relation.rb +5 -5
- data/lib/og/relation/has_many.rb +3 -1
- data/lib/og/relation/joins_many.rb +1 -1
- data/lib/og/store.rb +6 -3
- data/lib/og/store/kirby.rb +3 -5
- data/lib/og/store/mysql.rb +0 -1
- data/lib/og/store/sql.rb +43 -7
- data/lib/og/store/sqlite.rb +97 -11
- data/lib/og/store/sqlite2.rb +231 -0
- data/lib/og/test/testcase.rb +1 -1
- data/lib/og/vendor/mysql.rb +103 -25
- data/test/glue/tc_revisable.rb +11 -11
- data/test/og/CONFIG.rb +20 -8
- data/test/og/mixin/tc_hierarchical.rb +5 -3
- data/test/og/mixin/tc_optimistic_locking.rb +6 -4
- data/test/og/mixin/tc_orderable.rb +22 -22
- data/test/og/mixin/tc_taggable.rb +15 -11
- data/test/og/mixin/tc_timestamped.rb +4 -2
- data/test/og/multi_validations_model.rb +8 -0
- data/test/og/store/tc_filesys.rb +15 -12
- data/test/og/store/tc_kirby.rb +14 -11
- data/test/og/tc_accumulator.rb +1 -3
- data/test/og/tc_cacheable.rb +58 -0
- data/test/og/tc_delete_all.rb +13 -16
- data/test/og/tc_ez.rb +33 -0
- data/test/og/tc_finder.rb +2 -4
- data/test/og/tc_inheritance.rb +3 -3
- data/test/og/tc_inheritance2.rb +2 -3
- data/test/og/tc_join.rb +3 -2
- data/test/og/tc_multi_validations.rb +3 -3
- data/test/og/tc_multiple.rb +3 -6
- data/test/og/tc_override.rb +19 -13
- data/test/og/tc_polymorphic.rb +1 -3
- data/test/og/tc_resolve.rb +32 -0
- data/test/og/tc_reverse.rb +27 -28
- data/test/og/tc_scoped.rb +2 -4
- data/test/og/tc_select.rb +1 -3
- data/test/og/tc_store.rb +3 -8
- data/test/og/tc_validation.rb +2 -2
- data/test/og/tc_validation2.rb +56 -58
- data/test/og/tc_validation_loop.rb +2 -5
- metadata +15 -7
- data/lib/og/vendor/mysql411.rb +0 -306
@@ -0,0 +1,181 @@
|
|
1
|
+
# gmosx: Work in progress.
|
2
|
+
|
3
|
+
module Caboose
|
4
|
+
|
5
|
+
module EZ
|
6
|
+
# EZ::Condition plugin for generating the :conditions where clause
|
7
|
+
# for ActiveRecord::Base.find. And an extension to ActiveRecord::Base
|
8
|
+
# called AR::Base.find_with_conditions that takes a block and builds
|
9
|
+
# the where clause dynamically for you.
|
10
|
+
|
11
|
+
class Condition
|
12
|
+
# need this so that id doesn't call Object#id
|
13
|
+
# left it open to add more methods that
|
14
|
+
# conflict when I find them
|
15
|
+
[:id].each { |m| undef_method m }
|
16
|
+
|
17
|
+
# these are also reserved words regarding SQL column names
|
18
|
+
# use esc_* prefix to circumvent any issues
|
19
|
+
attr_reader :clauses
|
20
|
+
attr_accessor :inner
|
21
|
+
attr_accessor :outer
|
22
|
+
|
23
|
+
# Initialize @clauses and eval the block so
|
24
|
+
# it invokes method_missing.
|
25
|
+
def initialize(*args, &block)
|
26
|
+
options = args.last.is_a?(Hash) ? args.last : {}
|
27
|
+
options[:table_name] = args.first if args.first.kind_of? Symbol
|
28
|
+
@table_name = options.delete(:table_name) || nil
|
29
|
+
@outer = options.delete(:outer) || :and
|
30
|
+
@inner = options.delete(:inner) || :and
|
31
|
+
@clauses = []
|
32
|
+
instance_eval(&block) if block_given?
|
33
|
+
end
|
34
|
+
|
35
|
+
# When invoked with the name of the column in each statement inside the block:
|
36
|
+
# A new Clause instance is created and recieves the args. Then the operator
|
37
|
+
# hits method_missing and gets sent to a new Clause instance where it either
|
38
|
+
# matches one of the defined ops or hits method_missing there.
|
39
|
+
#
|
40
|
+
# When invoked with an attached block a subcondition is created. The name
|
41
|
+
# is regarded as the table_name, additional parameters for outer and inner
|
42
|
+
# are passed on.
|
43
|
+
def method_missing(name, *args, &block)
|
44
|
+
if block_given?
|
45
|
+
# handle name as table_name and create a subcondition
|
46
|
+
options = args.last.is_a?(Hash) ? args.last : {}
|
47
|
+
options[:table_name] ||= name
|
48
|
+
define_sub(options, &block)
|
49
|
+
else
|
50
|
+
clause(name, *args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# You can define clauses dynamicly using this method. It will take a
|
55
|
+
# clause and create the correct Clause object to process the conditions
|
56
|
+
def clause(name, *args)
|
57
|
+
if name.kind_of?(Array)
|
58
|
+
c = Clause.new(name.first, name.last)
|
59
|
+
elsif args.last.kind_of?(Symbol)
|
60
|
+
c = Clause.new(args.pop, name)
|
61
|
+
else
|
62
|
+
c = Clause.new(@table_name, name)
|
63
|
+
end
|
64
|
+
@clauses << c
|
65
|
+
c
|
66
|
+
end
|
67
|
+
|
68
|
+
# Create subcondition from a block, optionally specifying table_name, outer and inner.
|
69
|
+
# :outer determines how the subcondition is added to the condition, while :inner
|
70
|
+
# determines the internal 'joining' of conditions inside the subcondition. Both
|
71
|
+
# :inner & :outer defult to 'AND'
|
72
|
+
def define_sub(*args, &block)
|
73
|
+
options = args.last.is_a?(Hash) ? args.last : {}
|
74
|
+
options[:table_name] = args.first if args.first.kind_of? Symbol
|
75
|
+
options[:table_name] ||= @table_name
|
76
|
+
cond = Condition.new(options, &block)
|
77
|
+
self << cond
|
78
|
+
end
|
79
|
+
|
80
|
+
# Aliases for syntax convenience. :sub or :condition map to :define_sub
|
81
|
+
alias :sub :define_sub
|
82
|
+
alias :condition :define_sub
|
83
|
+
|
84
|
+
# Shortcut for adding a :and boolean joined subcondition
|
85
|
+
def and_condition(*args, &block)
|
86
|
+
options = args.last.is_a?(Hash) ? args.last : {}
|
87
|
+
options[:table_name] = args.first if args.first.kind_of? Symbol
|
88
|
+
options[:outer] ||= @outer
|
89
|
+
options[:inner] ||= :and
|
90
|
+
define_sub(options, &block)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Alias :all to be shorthand for :and_condition
|
94
|
+
alias :all :and_condition
|
95
|
+
|
96
|
+
# Shortcut for adding a :or boolean joined subcondition
|
97
|
+
def or_condition(*args, &block)
|
98
|
+
options = args.last.is_a?(Hash) ? args.last : {}
|
99
|
+
options[:table_name] = args.first if args.first.kind_of? Symbol
|
100
|
+
options[:outer] ||= @outer
|
101
|
+
options[:inner] ||= :or
|
102
|
+
define_sub(options, &block)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Alias :any to stand in for :or_condition
|
106
|
+
alias :any :or_condition
|
107
|
+
|
108
|
+
# Append a condition element, which can be one of the following:
|
109
|
+
# - String: raw sql string
|
110
|
+
# - ActiveRecord instance, for attribute or PK cloning
|
111
|
+
# - Condition or Clause with to_sql method and outer property
|
112
|
+
# - Array in ActiveRecord format ['column = ?', 2]
|
113
|
+
def <<(condition, outer = nil)
|
114
|
+
if condition.kind_of?(String) and not condition.to_s.empty?
|
115
|
+
cond = SqlClause.new(condition)
|
116
|
+
cond.outer = outer || :and
|
117
|
+
@clauses << cond
|
118
|
+
elsif condition.kind_of?(Og::EntityMixin)
|
119
|
+
if condition.attributes[condition.class.primary_key].nil?
|
120
|
+
condition.attributes.each { |k, v| clause([condition.class.table_name, k]) == v unless v.to_s.empty? }
|
121
|
+
else
|
122
|
+
clause([condition.class.table_name, condition.class.primary_key]) == condition.attributes[condition.class.primary_key]
|
123
|
+
end
|
124
|
+
else
|
125
|
+
if condition.kind_of?(Condition) or condition.kind_of?(AbstractClause)
|
126
|
+
logic = condition.outer if outer.nil?
|
127
|
+
condition = condition.to_sql
|
128
|
+
else
|
129
|
+
logic = outer
|
130
|
+
end
|
131
|
+
if condition.kind_of?(Array) and not condition.empty?
|
132
|
+
array_clause = ArrayClause.new(condition)
|
133
|
+
array_clause.outer = logic
|
134
|
+
@clauses << array_clause
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Aliases for :<<, the method itself deals with what kind
|
140
|
+
# of condition you are appending to the chain so these
|
141
|
+
# aliases are for a nicer syntax's sake.
|
142
|
+
alias :sql_condition :<<
|
143
|
+
alias :add_sql :<<
|
144
|
+
alias :clone_from :<<
|
145
|
+
alias :append :<<
|
146
|
+
|
147
|
+
# Loop over all Clause onjects in @clauses array
|
148
|
+
# and call to_sql on each instance. Then join
|
149
|
+
# the queries and params into the :conditions
|
150
|
+
# array with logic defaulting to AND.
|
151
|
+
# Subqueries are joined together using their
|
152
|
+
# individual outer property setting if present.
|
153
|
+
# Also defaults to AND.
|
154
|
+
def to_sql(logic=@inner)
|
155
|
+
params = []; query = []
|
156
|
+
@clauses.each do |cv|
|
157
|
+
q, p, e = cv.to_sql
|
158
|
+
unless q.to_s.empty?
|
159
|
+
logic = cv.outer ? cv.outer : logic
|
160
|
+
logic = logic.to_s.upcase
|
161
|
+
logic = 'AND NOT' if logic == 'NOT'
|
162
|
+
query << logic unless query.empty?
|
163
|
+
query << q
|
164
|
+
if cv.test == :in
|
165
|
+
params << p if p.respond_to?(:map)
|
166
|
+
elsif p.kind_of?(Array)
|
167
|
+
p.flatten! unless q =~ /IN/
|
168
|
+
params += p
|
169
|
+
else
|
170
|
+
params << p unless p.nil?
|
171
|
+
params << e unless e.nil?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
[query.join(' '), *params ]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
end # EZ module
|
180
|
+
|
181
|
+
end # Caboose module
|
data/lib/og/manager.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'facet/pool'
|
2
|
+
require 'facet/class/descendents'
|
3
3
|
|
4
4
|
require 'og/entity'
|
5
5
|
require 'og/store'
|
@@ -10,6 +10,15 @@ module Og
|
|
10
10
|
# managed by Og.
|
11
11
|
|
12
12
|
class Manager
|
13
|
+
def self.managers
|
14
|
+
managers = []
|
15
|
+
ObjectSpace.each_object(self) { |o| managers << o }
|
16
|
+
managers
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.managed?(klass)
|
20
|
+
self.managers.any? { |m| m.managed? klass }
|
21
|
+
end
|
13
22
|
|
14
23
|
# Information about an Entity class.
|
15
24
|
|
@@ -34,9 +43,15 @@ class Manager
|
|
34
43
|
|
35
44
|
attr_accessor :store
|
36
45
|
|
37
|
-
# The collection of Entities managed by
|
46
|
+
# The collection of Entities (managed classes) managed by
|
47
|
+
# this manager.
|
38
48
|
|
39
49
|
attr_accessor :entities
|
50
|
+
|
51
|
+
# The managed object cache. This cache is optional. When
|
52
|
+
# used it improves object lookups.
|
53
|
+
|
54
|
+
attr_accessor :cache
|
40
55
|
|
41
56
|
def initialize(options)
|
42
57
|
@options = options
|
@@ -135,7 +150,7 @@ class Manager
|
|
135
150
|
#++
|
136
151
|
|
137
152
|
def manageable?(klass)
|
138
|
-
klass.respond_to?(:properties) and (!klass.properties.empty?) # and klass.ann.self.polymorphic.nil?
|
153
|
+
klass.respond_to?(:properties) and (!klass.properties.empty?) # and !self.class.managed?(klass) # and klass.ann.self.polymorphic.nil?
|
139
154
|
end
|
140
155
|
|
141
156
|
# Is the class managed by Og?
|
@@ -145,9 +160,8 @@ class Manager
|
|
145
160
|
end
|
146
161
|
alias_method :entity?, :managed?
|
147
162
|
|
148
|
-
# ==
|
149
163
|
# Returns an array containing all classes managed by this manager.
|
150
|
-
|
164
|
+
|
151
165
|
def managed_classes
|
152
166
|
@entities.map {|e| e[0]}
|
153
167
|
end
|
@@ -186,29 +200,6 @@ class Manager
|
|
186
200
|
classes.each { |c| Relation.resolve_targets(c) }
|
187
201
|
classes.each { |c| Relation.resolve_names(c) }
|
188
202
|
classes.each { |c| manage(c) }
|
189
|
-
=begin
|
190
|
-
# gmosx: LETS better investigate this.
|
191
|
-
|
192
|
-
# Checks for obsolete tables lying around in the og store.
|
193
|
-
# rp: this shouldn't really be here but I can't think of a better
|
194
|
-
# way to add this functionality.
|
195
|
-
|
196
|
-
store = get_store()
|
197
|
-
|
198
|
-
if store.respond_to? :unmanaged_tables
|
199
|
-
unmanaged_tables = store.unmanaged_tables(self)
|
200
|
-
unmanaged_tables.each do |table|
|
201
|
-
|
202
|
-
if @options[:evolve_schema] == true and @options[:evolve_schema_purge_tables] == true
|
203
|
-
sql = "DROP TABLE #{table}"
|
204
|
-
Logger.debug "Dropping unmanaged database table #{table}"
|
205
|
-
store.conn.exec(sql)
|
206
|
-
else
|
207
|
-
Logger.info "There is a table within the database named '#{table}' that is not managed by an Og class."
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
=end
|
212
203
|
end
|
213
204
|
alias_method :manage_class, :manage_classes
|
214
205
|
|
@@ -233,7 +224,17 @@ class Manager
|
|
233
224
|
def post_setup
|
234
225
|
store.post_setup if store.respond_to?(:post_setup)
|
235
226
|
end
|
236
|
-
|
227
|
+
|
228
|
+
# Dump a nice name for this store.
|
229
|
+
=begin
|
230
|
+
def to_s
|
231
|
+
if store = get_store
|
232
|
+
store.to_s
|
233
|
+
else
|
234
|
+
'Uninitialized'
|
235
|
+
end
|
236
|
+
end
|
237
|
+
=end
|
237
238
|
end
|
238
239
|
|
239
240
|
end
|
data/lib/og/relation.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
1
|
+
require 'facet/kernel/constant'
|
2
|
+
require 'facet/string/capitalized'
|
3
|
+
require 'facet/ormsupport'
|
4
|
+
require 'facet/inheritor'
|
5
|
+
require 'facet/annotation'
|
6
6
|
|
7
7
|
module Og
|
8
8
|
|
data/lib/og/relation/has_many.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'facet/ormsupport'
|
2
2
|
|
3
3
|
require 'og/relation'
|
4
4
|
require 'og/collection'
|
@@ -56,6 +56,8 @@ class HasMany < Relation
|
|
56
56
|
|
57
57
|
def add_#{target_singular_name}(obj, options = nil)
|
58
58
|
return unless obj
|
59
|
+
# save the object if needed to generate a primary_key.
|
60
|
+
self.save unless self.saved?
|
59
61
|
obj.#{foreign_key} = @#{owner_class.primary_key}
|
60
62
|
obj.save
|
61
63
|
end
|
data/lib/og/store.rb
CHANGED
@@ -50,9 +50,12 @@ class Store
|
|
50
50
|
# Enchants a class.
|
51
51
|
|
52
52
|
def enchant(klass, manager)
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
# gmosx, FIXME:
|
54
|
+
# ARGH, have to update this!
|
55
|
+
#
|
56
|
+
# klass.class.send(:define_method, :index) do |arg|
|
57
|
+
# meta :index, arg
|
58
|
+
# end
|
56
59
|
|
57
60
|
pk = klass.primary_key.symbol
|
58
61
|
|
data/lib/og/store/kirby.rb
CHANGED
@@ -68,14 +68,10 @@ class KirbyStore < SqlStore
|
|
68
68
|
klass.send :alias_method, :recno, :oid
|
69
69
|
klass.send :alias_method, :recno=, :oid=
|
70
70
|
|
71
|
-
unless klass.properties.include? :recno
|
72
|
-
klass.property :recno, Fixnum
|
73
|
-
end
|
74
|
-
|
75
71
|
symbols = klass.properties.keys
|
76
72
|
|
77
73
|
klass.module_eval %{
|
78
|
-
def self.kb_create(#{symbols.join(', ')})
|
74
|
+
def self.kb_create(recno, #{symbols.join(', ')})
|
79
75
|
obj = self.allocate
|
80
76
|
obj.recno = recno
|
81
77
|
#{ symbols.map { |s| "obj.#{s} = #{s}; "} }
|
@@ -281,6 +277,8 @@ private
|
|
281
277
|
|
282
278
|
def eval_og_insert(klass)
|
283
279
|
pk = klass.primary_key.symbol
|
280
|
+
props = klass.properties.values.dup
|
281
|
+
values = props.collect { |p| write_prop(p) }.join(',')
|
284
282
|
|
285
283
|
if klass.schema_inheritance?
|
286
284
|
props << Property.new(:symbol => :ogtype, :klass => String)
|
data/lib/og/store/mysql.rb
CHANGED
data/lib/og/store/sql.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'time'
|
3
3
|
|
4
|
-
require '
|
4
|
+
require 'facet/kernel/constant'
|
5
5
|
|
6
6
|
module Og
|
7
7
|
|
8
|
+
# A collection of useful SQL utilities.
|
9
|
+
|
8
10
|
module SqlUtils
|
9
11
|
|
10
12
|
# Escape an SQL string
|
@@ -174,7 +176,6 @@ module SqlUtils
|
|
174
176
|
end
|
175
177
|
|
176
178
|
def join_table_info(owner_class, target_class, postfix = nil)
|
177
|
-
|
178
179
|
# some fixes for schema inheritance.
|
179
180
|
|
180
181
|
raise "Undefined owner_class in #{target_class}" unless owner_class
|
@@ -304,7 +305,6 @@ class SqlStore < Store
|
|
304
305
|
# Enchants a class.
|
305
306
|
|
306
307
|
def enchant(klass, manager)
|
307
|
-
|
308
308
|
# setup the table where this class is mapped.
|
309
309
|
|
310
310
|
if klass.schema_inheritance_child?
|
@@ -314,6 +314,11 @@ class SqlStore < Store
|
|
314
314
|
klass.const_set 'OGTABLE', table(klass)
|
315
315
|
end
|
316
316
|
|
317
|
+
#--
|
318
|
+
# FIXME: use an SQL agnostic name like schema instead
|
319
|
+
# of table.
|
320
|
+
#++
|
321
|
+
|
317
322
|
klass.module_eval 'def self.table; OGTABLE; end'
|
318
323
|
|
319
324
|
eval_og_allocate(klass)
|
@@ -322,12 +327,14 @@ class SqlStore < Store
|
|
322
327
|
|
323
328
|
unless klass.polymorphic_parent?
|
324
329
|
# precompile class specific lifecycle methods.
|
330
|
+
|
325
331
|
eval_og_create_schema(klass)
|
326
332
|
eval_og_insert(klass)
|
327
333
|
eval_og_update(klass)
|
328
334
|
eval_og_delete(klass)
|
329
335
|
|
330
336
|
# create the table if needed.
|
337
|
+
|
331
338
|
klass.allocate.og_create_schema(self)
|
332
339
|
|
333
340
|
# finish up with eval_og_read, since we can't do that
|
@@ -335,7 +342,8 @@ class SqlStore < Store
|
|
335
342
|
# Possible FIXME: This means you can't do any find-type
|
336
343
|
# operations in og_create_schema. Luckily, you're most
|
337
344
|
# likely to want to do .create, which is covered by
|
338
|
-
|
345
|
+
# og_insert.
|
346
|
+
|
339
347
|
eval_og_read(klass)
|
340
348
|
end
|
341
349
|
end
|
@@ -828,8 +836,21 @@ private
|
|
828
836
|
end
|
829
837
|
|
830
838
|
# Resolve the finder options. Also takes scope into account.
|
839
|
+
# This method handles among other the following cases:
|
840
|
+
#
|
841
|
+
# User.find :condition => "name LIKE 'g%'", :order => 'name ASC'
|
842
|
+
# User.find :where => "name LIKE 'g%'", :order => 'name ASC'
|
843
|
+
# User.find :sql => "WHERE name LIKE 'g%' ORDER BY name ASC"
|
844
|
+
# User.find :condition => [ 'name LIKE ?', 'g%' ], :order => 'name ASC', :limit => 10
|
845
|
+
#
|
846
|
+
# If an array is passed as a condition, use prepared statement
|
847
|
+
# style escaping. For example:
|
848
|
+
#
|
849
|
+
# User.find :condition => [ 'name = ? AND age > ?', 'gmosx', 12 ]
|
850
|
+
#
|
851
|
+
# Proper escaping is performed to avoid SQL injection attacks.
|
831
852
|
#--
|
832
|
-
# FIXME: cleanup/refactor.
|
853
|
+
# FIXME: cleanup/refactor, this is an IMPORTANT method.
|
833
854
|
#++
|
834
855
|
|
835
856
|
def resolve_options(klass, options)
|
@@ -888,7 +909,9 @@ private
|
|
888
909
|
|
889
910
|
update_condition(options, scond) if scond
|
890
911
|
|
891
|
-
# rp: type is not set in all instances such as Class.first
|
912
|
+
# rp: type is not set in all instances such as Class.first
|
913
|
+
# so this fix goes here for now.
|
914
|
+
|
892
915
|
if ogtype = options[:type] || (klass.schema_inheritance_child? ? "#{klass}" : nil)
|
893
916
|
update_condition options, "ogtype='#{ogtype}'"
|
894
917
|
end
|
@@ -896,6 +919,16 @@ private
|
|
896
919
|
sql = "SELECT #{fields} FROM #{tables.join(',')}"
|
897
920
|
|
898
921
|
if condition = options[:condition] || options[:where]
|
922
|
+
# If an array is passed as a condition, use prepared
|
923
|
+
# statement style escaping.
|
924
|
+
|
925
|
+
if condition.is_a?(Array)
|
926
|
+
args = condition.dup
|
927
|
+
str = args.shift
|
928
|
+
args.each { |arg| str.sub!(/\?/, quote(arg)) }
|
929
|
+
condition = str
|
930
|
+
end
|
931
|
+
|
899
932
|
sql << " WHERE #{condition}"
|
900
933
|
end
|
901
934
|
|
@@ -969,7 +1002,10 @@ private
|
|
969
1002
|
end
|
970
1003
|
|
971
1004
|
res_row = res.next
|
972
|
-
|
1005
|
+
|
1006
|
+
# causes STI classes to come back as the correct child class
|
1007
|
+
# if accessed from the superclass.
|
1008
|
+
|
973
1009
|
klass = Og::Entity::entity_from_string(res_row.result.flatten[res_row.fieldnum('ogtype')]) if klass.schema_inheritance?
|
974
1010
|
obj = klass.og_allocate(res_row, 0)
|
975
1011
|
|