og 0.27.0 → 0.28.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.
- 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
|
|