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
data/lib/glue/taggable.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'facet/inflect'
|
2
2
|
|
3
3
|
# The default Tag implementation. A tag attaches semantics to
|
4
4
|
# a given object.
|
@@ -25,7 +25,10 @@ class Tag
|
|
25
25
|
#++
|
26
26
|
|
27
27
|
def tag(obj)
|
28
|
-
|
28
|
+
class_name = obj.class.name
|
29
|
+
method_name = class_name.index('::') ? (class_name =~ /.*?\:\:(.*)/; $1) : class_name
|
30
|
+
|
31
|
+
send(method_name.pluralize.underscore.to_sym) << obj
|
29
32
|
@count += 1
|
30
33
|
save!
|
31
34
|
end
|
@@ -119,7 +122,7 @@ module Taggable
|
|
119
122
|
def find_with_tags(*names)
|
120
123
|
info = ogmanager.store.join_table_info(self, Tag)
|
121
124
|
count = names.size
|
122
|
-
names = names.map { |n|
|
125
|
+
names = names.map { |n| ogmanager.store.quote(n) }.join(',')
|
123
126
|
sql = %{
|
124
127
|
SELECT o.*
|
125
128
|
FROM
|
@@ -142,7 +145,7 @@ module Taggable
|
|
142
145
|
def find_with_any_tag(*names)
|
143
146
|
info = ogmanager.store.join_table_info(self, Tag)
|
144
147
|
count = names.size
|
145
|
-
names = names.map { |n|
|
148
|
+
names = names.map { |n| ogmanager.store.quote(n) }.join(',')
|
146
149
|
sql = %{
|
147
150
|
SELECT o.*
|
148
151
|
FROM
|
data/lib/glue/timestamped.rb
CHANGED
data/lib/og.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# = Og
|
2
2
|
#
|
3
|
-
# Copyright (c) 2004-2005, Navel Ltd (http://www.navel.gr)
|
4
3
|
# Copyright (c) 2004-2005, George Moschovitis (http://www.gmosx.com)
|
4
|
+
# Copyright (c) 2004-2005, Navel Ltd (http://www.navel.gr)
|
5
5
|
#
|
6
6
|
# Og (http://www.nitrohq.com) is copyrighted free software
|
7
7
|
# created and maintained by George Moschovitis (mailto:gm@navel.gr)
|
8
8
|
# and released under the standard BSD Licence. For details
|
9
9
|
# consult the file doc/LICENCE.
|
10
10
|
|
11
|
-
require '
|
12
|
-
require '
|
11
|
+
require 'facet/synchash'
|
12
|
+
require 'facet/syncarray'
|
13
13
|
|
14
14
|
require 'glue'
|
15
15
|
require 'glue/logger'
|
@@ -43,7 +43,7 @@ module Og
|
|
43
43
|
|
44
44
|
# The version.
|
45
45
|
|
46
|
-
Version = '0.
|
46
|
+
Version = '0.28.0'
|
47
47
|
|
48
48
|
# Library path.
|
49
49
|
|
@@ -95,6 +95,14 @@ module Og
|
|
95
95
|
|
96
96
|
setting :thread_safe, :default => true, :doc => 'Enable/dissable thread safe mode'
|
97
97
|
|
98
|
+
# Address of the Og cache (if distributed caching enabled).
|
99
|
+
|
100
|
+
setting :cache_address, :default => '127.0.0.1', :doc => 'Address of the Og cache'
|
101
|
+
|
102
|
+
# Port of the Og cache (if distributed caching enabled).
|
103
|
+
|
104
|
+
setting :cache_port, :default => '9070', :doc => 'Port of the Og cache'
|
105
|
+
|
98
106
|
# The active manager
|
99
107
|
|
100
108
|
mattr_accessor :manager
|
@@ -119,7 +127,7 @@ module Og
|
|
119
127
|
|
120
128
|
m = @@manager = Manager.new(options)
|
121
129
|
m.manage_classes
|
122
|
-
|
130
|
+
|
123
131
|
# Allows functionality that requires a store is
|
124
132
|
# finalized to be implemented. A vastly superior
|
125
133
|
# method of constructing foreign key constraints is an
|
@@ -147,7 +155,6 @@ module Og
|
|
147
155
|
def escape(str)
|
148
156
|
@@manager.store.escape(str)
|
149
157
|
end
|
150
|
-
|
151
158
|
end
|
152
159
|
|
153
160
|
end
|
data/lib/og/entity.rb
CHANGED
@@ -1,30 +1,55 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'facet/classinherit'
|
2
|
+
require 'facet/kernel/assign_with'
|
3
3
|
|
4
4
|
require 'glue/property'
|
5
5
|
require 'og/relation'
|
6
|
+
require 'og/ez/clause'
|
7
|
+
require 'og/ez/condition'
|
6
8
|
|
7
9
|
module Og
|
8
10
|
|
9
11
|
# Include this module to classes to make them managable by Og.
|
12
|
+
#--
|
13
|
+
# gmosx, WARTNING: If you change the methods here, don't
|
14
|
+
# forget to update the Cacheable overrides.
|
15
|
+
#++
|
10
16
|
|
11
17
|
module EntityMixin
|
12
18
|
|
19
|
+
def self.included(base)
|
20
|
+
# If the after_enchant callback is defined, call it
|
21
|
+
# to allow for some customization. Have a look at cacheable
|
22
|
+
# for an example.
|
23
|
+
|
24
|
+
if base.respond_to?(:after_enchant)
|
25
|
+
base.after_enchant(base)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Persist the object.
|
30
|
+
|
13
31
|
def save(options = nil)
|
14
32
|
self.class.ogmanager.store.save(self, options)
|
15
33
|
end
|
16
34
|
alias_method :save!, :save
|
17
35
|
alias_method :validate_and_save, :save
|
18
36
|
|
37
|
+
# Force saving of the objects, even if the validations
|
38
|
+
# don't pass.
|
39
|
+
|
19
40
|
def force_save!(options = nil)
|
20
41
|
self.class.ogmanager.store.force_save(self, options)
|
21
42
|
end
|
22
43
|
|
44
|
+
# Insert the object in the store.
|
45
|
+
|
23
46
|
def insert
|
24
47
|
self.class.ogmanager.store.insert(self)
|
25
48
|
return self
|
26
49
|
end
|
27
50
|
|
51
|
+
# Update an existing object in the store.
|
52
|
+
|
28
53
|
def update(options = nil)
|
29
54
|
self.class.ogmanager.store.update(self, options)
|
30
55
|
end
|
@@ -59,15 +84,13 @@ module EntityMixin
|
|
59
84
|
self.class.ogmanager.store.transaction(&block)
|
60
85
|
end
|
61
86
|
|
87
|
+
# Is this object saved in the store?
|
88
|
+
|
62
89
|
def saved?
|
63
90
|
not @oid.nil?
|
64
91
|
end
|
65
92
|
alias_method :serialized?, :saved?
|
66
93
|
|
67
|
-
def og_quote(obj)
|
68
|
-
self.class.ogmanager.store.quote(obj)
|
69
|
-
end
|
70
|
-
|
71
94
|
def assign_properties(values, options = {})
|
72
95
|
Property.populate_object(self, values, options)
|
73
96
|
return self
|
@@ -84,6 +107,14 @@ module EntityMixin
|
|
84
107
|
end
|
85
108
|
end
|
86
109
|
|
110
|
+
def og_quote(obj)
|
111
|
+
self.class.ogmanager.store.quote(obj)
|
112
|
+
end
|
113
|
+
|
114
|
+
def og_clone(*args)
|
115
|
+
Og::Entity.clone(self,*args)
|
116
|
+
end
|
117
|
+
|
87
118
|
include RelationDSL
|
88
119
|
|
89
120
|
class_inherit do
|
@@ -129,8 +160,25 @@ module EntityMixin
|
|
129
160
|
|
130
161
|
# Find a specific instance of this class according
|
131
162
|
# to the given conditions.
|
163
|
+
#
|
164
|
+
# Unlike the lower level store.find method it accepts
|
165
|
+
# Strings and Arrays instead of an option hash.
|
166
|
+
#
|
167
|
+
# === Examples
|
168
|
+
#
|
169
|
+
# User.find :condition => "name LIKE 'g%'", :order => 'name ASC'
|
170
|
+
# User.find :where => "name LIKE 'g%'", :order => 'name ASC'
|
171
|
+
# User.find :sql => "WHERE name LIKE 'g%' ORDER BY name ASC"
|
172
|
+
# User.find :condition => [ 'name LIKE ?', 'g%' ], :order => 'name ASC', :limit => 10
|
173
|
+
# User.find "name LIKE 'g%'"
|
174
|
+
# User.find "WHERE name LIKE 'g%' LIMIT 10"
|
175
|
+
# User.find [ 'name LIKE ?', 'g%' ]
|
132
176
|
|
133
|
-
def find(options = {})
|
177
|
+
def find(options = {}, &block)
|
178
|
+
options = resolve_non_hash_options(options)
|
179
|
+
|
180
|
+
ez_resolve_options(options, &block) if block_given?
|
181
|
+
|
134
182
|
if find_options = self.ann.self[:find_options]
|
135
183
|
options = find_options.dup.update(options)
|
136
184
|
end
|
@@ -144,17 +192,31 @@ module EntityMixin
|
|
144
192
|
|
145
193
|
# Find a single instance of this class.
|
146
194
|
|
147
|
-
def find_one(options = {})
|
195
|
+
def find_one(options = {}, &block)
|
196
|
+
options = resolve_non_hash_options(options)
|
197
|
+
|
198
|
+
ez_resolve_options(options, &block) if block_given?
|
199
|
+
|
200
|
+
if find_options = self.ann.self[:find_options]
|
201
|
+
options = find_options.dup.update(options)
|
202
|
+
end
|
203
|
+
|
148
204
|
options[:class] = self
|
205
|
+
options[:type] = self if self.schema_inheritance_child?
|
206
|
+
|
149
207
|
ogmanager.store.find_one(options)
|
150
208
|
end
|
151
209
|
alias_method :one, :find_one
|
152
210
|
alias_method :first, :find_one
|
153
211
|
|
212
|
+
# Select an object using an sql query.
|
213
|
+
|
154
214
|
def select(sql)
|
155
215
|
ogmanager.store.select(sql, self)
|
156
216
|
end
|
157
217
|
|
218
|
+
# Select one instance using an sqll query.
|
219
|
+
|
158
220
|
def select_one(sql)
|
159
221
|
ogmanager.store.select_one(sql, self)
|
160
222
|
end
|
@@ -375,6 +437,60 @@ module EntityMixin
|
|
375
437
|
|
376
438
|
private
|
377
439
|
|
440
|
+
# Resolve String/Array options.
|
441
|
+
#--
|
442
|
+
# FIXME: move to sql store?
|
443
|
+
#++
|
444
|
+
|
445
|
+
def resolve_non_hash_options(options)
|
446
|
+
if options.is_a? String
|
447
|
+
if options =~ /^WHERE/i
|
448
|
+
# pass the string as sql.
|
449
|
+
return { :sql => options }
|
450
|
+
else
|
451
|
+
# pass the string as a condition.
|
452
|
+
return { :condition => options }
|
453
|
+
end
|
454
|
+
elsif options.is_a? Array
|
455
|
+
# pass the array as condition (prepared statement style
|
456
|
+
# parsing/quoting.
|
457
|
+
return { :condition => options }
|
458
|
+
end
|
459
|
+
|
460
|
+
return options
|
461
|
+
end
|
462
|
+
|
463
|
+
# Resolve ez options, ie options provided using the
|
464
|
+
# Ruby query language.
|
465
|
+
#--
|
466
|
+
# gmosx: investigate this.
|
467
|
+
#++
|
468
|
+
|
469
|
+
def ez_resolve_options(options, &block)
|
470
|
+
klass = self.name.downcase.to_sym
|
471
|
+
# conditions on self first
|
472
|
+
# conditions = [ez_condition(:outer => outer_mapping[klass], :inner => inner_mapping[klass])]
|
473
|
+
conditions = [ez_condition()]
|
474
|
+
|
475
|
+
# options[:include].uniq.each do |assoc|
|
476
|
+
# conditions << reflect_on_association(assoc).klass.ez_condition(:outer => outer_mapping[assoc], :inner => inner_mapping[assoc])
|
477
|
+
# end
|
478
|
+
|
479
|
+
yield *conditions
|
480
|
+
|
481
|
+
condition = Caboose::EZ::Condition.new
|
482
|
+
conditions.each { |c| condition << c }
|
483
|
+
options[:condition] = condition.to_sql
|
484
|
+
# p options[:conditions] if $DEBUG
|
485
|
+
end
|
486
|
+
|
487
|
+
# Returns a Condition for this object.
|
488
|
+
|
489
|
+
def ez_condition(options = {}, &block)
|
490
|
+
options[:table_name] ||= table()
|
491
|
+
Caboose::EZ::Condition.new(options, &block)
|
492
|
+
end
|
493
|
+
|
378
494
|
# Helper method for dynamic finders. Finds the object dynamically parsed
|
379
495
|
# method name is after.
|
380
496
|
|
@@ -386,7 +502,11 @@ private
|
|
386
502
|
options = args.pop if args.last.is_a?(Hash)
|
387
503
|
|
388
504
|
condition = attrs.zip(args).map do |name, value|
|
389
|
-
|
505
|
+
field_name = properties[name.to_sym][:field] ||
|
506
|
+
properties[name.to_sym][:name] ||
|
507
|
+
properties[name.to_sym][:symbol]
|
508
|
+
|
509
|
+
%|#{field_name} #{options.delete("#{name}_op".to_sym) || '='} #{ogmanager.store.quote(value)}|
|
390
510
|
end.join(' AND ')
|
391
511
|
|
392
512
|
options.update(
|
@@ -448,6 +568,102 @@ class Entity
|
|
448
568
|
res
|
449
569
|
end
|
450
570
|
|
571
|
+
# Entity copying support. Eventually this should all
|
572
|
+
# be eval'd in at enchanting stage for the minor
|
573
|
+
# speed increase.
|
574
|
+
# TODO: Convert to enchantments on objects
|
575
|
+
|
576
|
+
# Accepts source object, destination and ignore.
|
577
|
+
# Source and destination are self explanatory; ignore
|
578
|
+
# is a list of properties not to copy (i.e.
|
579
|
+
# :create_time,:update_time).
|
580
|
+
# By default sets the class variables directly on the
|
581
|
+
# remote model instance, if you set use_setter_method to
|
582
|
+
# true, uses create_time= style copying tactics,
|
583
|
+
|
584
|
+
def copy_properties(source, destination, ignore = [], use_setter_method = false)
|
585
|
+
property_copier(source, destination, ignore, use_setter_method, false)
|
586
|
+
end
|
587
|
+
|
588
|
+
# Copies relations of one record to another. Only copies
|
589
|
+
# has_one, refers_to, belongs_to relationships as
|
590
|
+
# has_many requires modifying of other objects and
|
591
|
+
# cannot be copied (by design). If you think you need to copy
|
592
|
+
# these relations, what you need is a joins_many relationship
|
593
|
+
# which can be copied.
|
594
|
+
|
595
|
+
def copy_inferior_relations(source, destination, ignore = [])
|
596
|
+
real_ignore = Array.new
|
597
|
+
|
598
|
+
# Map relation symbols to foreign keys.
|
599
|
+
|
600
|
+
ignore.each do |symbol|
|
601
|
+
source.class.relations.reject{|r| [Og::JoinsMany, Og::ManyToMany, Og::HasMany].include?(r.class)}.each do |relation|
|
602
|
+
if relation.name == symbol.to_s
|
603
|
+
real_ignore << relation.foreign_key.to_sym
|
604
|
+
break
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
# Use instance variable property copier method.
|
610
|
+
|
611
|
+
property_copier(source, destination, real_ignore, false, true)
|
612
|
+
end
|
613
|
+
|
614
|
+
def copy_equal_relations(source, destination, ignore = [])
|
615
|
+
source.class.relations.reject{|r| not [Og::JoinsMany, Og::ManyToMany].include?(r.class)}.each do |relation|
|
616
|
+
next if relation.name == nil or ignore.include?(relation.name)
|
617
|
+
source.send(relation.name).each do |related|
|
618
|
+
destination.send(relation.name).send(:<<, related)
|
619
|
+
end
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
# Copies all relations *except* HasMany which is impossible
|
624
|
+
# to copy. Use a JoinsMany relation instead if you need a
|
625
|
+
# copyable HasMany (which is irrational).
|
626
|
+
|
627
|
+
def copy_relations(source, destination, ignore = [])
|
628
|
+
copy_inferior_relations(source, destination, ignore)
|
629
|
+
copy_equal_relations(source, destination, ignore)
|
630
|
+
end
|
631
|
+
|
632
|
+
# Clones an object in every possible way (cannot copy
|
633
|
+
# HasMany but can copy all others - BelongsTo, etc).
|
634
|
+
# Provide a source object as first arguments, the rest
|
635
|
+
# (if any) are passed along to the initialize constructor
|
636
|
+
# when calling new to make the copied object.
|
637
|
+
|
638
|
+
def clone(source,*args)
|
639
|
+
destination = source.class.new(*args)
|
640
|
+
copy_properties(source, destination, [], false)
|
641
|
+
# Must save here to copy join tables.
|
642
|
+
destination.save!
|
643
|
+
copy_relations(source, destination, [])
|
644
|
+
destination.save!
|
645
|
+
destination
|
646
|
+
end
|
647
|
+
|
648
|
+
# Does the work of clone_properties and copy_inferior_relations.
|
649
|
+
# Syntax is the same with one extra field to tell the
|
650
|
+
# routine what it is copying.
|
651
|
+
|
652
|
+
def property_copier(source,destination,ignore,use_setter_method,relations)
|
653
|
+
primary_key_symbol = source.class.primary_key.symbol
|
654
|
+
source.class.properties.to_a.each do |symbol, property|
|
655
|
+
next if primary_key_symbol == symbol or ignore.include?(symbol) or
|
656
|
+
(relations and not property.relation) or (not relations and property.relation)
|
657
|
+
|
658
|
+
variable = "@#{symbol}"
|
659
|
+
if use_setter_method
|
660
|
+
destination.send("#{symbol}=".to_sym,source.instance_variable_get(variable))
|
661
|
+
else
|
662
|
+
destination.instance_variable_set(variable, source.instance_variable_get(variable))
|
663
|
+
end
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
451
667
|
end
|
452
668
|
|
453
669
|
end
|
@@ -456,3 +672,4 @@ end
|
|
456
672
|
|
457
673
|
# * George Moschovitis <gm@navel.gr>
|
458
674
|
# * Tom Sawyer <transfire@gmail.com>
|
675
|
+
# * Rob Pitt <rob@motionpath.com>
|
data/lib/og/evolution.rb
CHANGED
data/lib/og/ez/clause.rb
ADDED
@@ -0,0 +1,147 @@
|
|
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 AbstractClause
|
12
|
+
|
13
|
+
attr_reader :test
|
14
|
+
attr_accessor :outer
|
15
|
+
|
16
|
+
def to_sql; 'TRUE'; end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class Clause < AbstractClause
|
21
|
+
# need this so that id doesn't call Object#id
|
22
|
+
# left it open to add more methods that
|
23
|
+
# conflict when I find them
|
24
|
+
[:id].each { |m| undef_method m }
|
25
|
+
|
26
|
+
attr_reader :name, :test, :value
|
27
|
+
|
28
|
+
# Initialize a Clause object with the name of the
|
29
|
+
# column.
|
30
|
+
def initialize(*args)
|
31
|
+
@table_prefix = ''
|
32
|
+
@negate = false
|
33
|
+
case args.length
|
34
|
+
when 0:
|
35
|
+
raise 'Expected at least one parameter'
|
36
|
+
when 1:
|
37
|
+
@name = args.first.to_s
|
38
|
+
when 2:
|
39
|
+
@table_prefix = args[0].to_s + '.' unless args[0].to_s.empty?
|
40
|
+
@name = args[1].to_s
|
41
|
+
when 3:
|
42
|
+
@table_prefix = args[0].to_s + '.' unless args[0].to_s.empty?
|
43
|
+
@name = args[1].to_s
|
44
|
+
@negate = args[2]
|
45
|
+
end
|
46
|
+
# append ! to negate the statement
|
47
|
+
if @name[-1] == '!'
|
48
|
+
@negate = true
|
49
|
+
@name = @name.slice(0, @name.length - 1)
|
50
|
+
end
|
51
|
+
# prefix with esc_ to avoid clashes with standard methods like 'alias'
|
52
|
+
@name = @name.slice(4, @name.length) if @name =~ /^esc_.*/
|
53
|
+
end
|
54
|
+
|
55
|
+
# The == operator has been over-ridden here to
|
56
|
+
# stand in for an exact match ["foo = ?", "bar"]
|
57
|
+
def ==(other)
|
58
|
+
@test = :equals
|
59
|
+
@value = other
|
60
|
+
end
|
61
|
+
|
62
|
+
# The =~ operator has been over-ridden here to
|
63
|
+
# stand in for the sql LIKE "%foobar%" clause.
|
64
|
+
def =~(pattern)
|
65
|
+
@test = :like
|
66
|
+
@value = pattern
|
67
|
+
end
|
68
|
+
|
69
|
+
# The spaceship <=> operator has been over-ridden here to
|
70
|
+
# stand in for the sql ["BETWEEN ? AND ?", 1, 5] "%foobar%" clause.
|
71
|
+
def <=>(range)
|
72
|
+
@test = :between
|
73
|
+
@value = range
|
74
|
+
end
|
75
|
+
|
76
|
+
# The === operator has been over-ridden here to
|
77
|
+
# stand in for the sql ["IN (?)", [1,2,3]] clause.
|
78
|
+
def ===(range)
|
79
|
+
@test = :in
|
80
|
+
@value = range
|
81
|
+
end
|
82
|
+
|
83
|
+
# switch on @test and build appropriate clause to
|
84
|
+
# match the operation.
|
85
|
+
def to_sql
|
86
|
+
case @test
|
87
|
+
when :equals
|
88
|
+
if @value == :null
|
89
|
+
@negate ? ["#{@table_prefix}#{@name} IS NOT NULL"] : ["#{@table_prefix}#{@name} IS NULL"]
|
90
|
+
else
|
91
|
+
@negate ? ["#{@table_prefix}#{@name} != ?", @value] : ["#{@table_prefix}#{@name} = ?", @value]
|
92
|
+
end
|
93
|
+
when :like
|
94
|
+
@negate ? ["#{@table_prefix}#{@name} NOT LIKE ?", @value] : ["#{@table_prefix}#{@name} LIKE ?", @value]
|
95
|
+
when :between
|
96
|
+
@negate ? ["#{@table_prefix}#{@name} NOT BETWEEN ? AND ?", @value.begin, @value.end] : ["#{@table_prefix}#{@name} BETWEEN ? AND ?", @value.begin, @value.end]
|
97
|
+
when :in
|
98
|
+
@negate ? ["#{@table_prefix}#{@name} NOT IN (?)", @value.to_a] : ["#{@table_prefix}#{@name} IN (?)", @value.to_a]
|
99
|
+
else
|
100
|
+
["#{@table_prefix}#{@name} #{@test} ?", @value]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# This method_missing takes care of setting
|
105
|
+
# @test to any operator thats not covered
|
106
|
+
# above. And @value to the value
|
107
|
+
def method_missing(name, *args)
|
108
|
+
@test = name
|
109
|
+
@value = args.first
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class ArrayClause < AbstractClause
|
114
|
+
|
115
|
+
# wraps around an Array in ActiveRecord format ['column = ?', 2]
|
116
|
+
|
117
|
+
def initialize(cond_array)
|
118
|
+
@test = :array
|
119
|
+
@cond_array = cond_array
|
120
|
+
end
|
121
|
+
|
122
|
+
def to_sql
|
123
|
+
return nil if @cond_array.first.to_s.empty?
|
124
|
+
query = (@cond_array.first =~ /^\([^\(\)]+\)$/) ? "#{@cond_array.first}" : "(#{@cond_array.first})"
|
125
|
+
[query, @cond_array[1..@cond_array.length] ]
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
class SqlClause < AbstractClause
|
131
|
+
|
132
|
+
# wraps around a raw SQL string
|
133
|
+
|
134
|
+
def initialize(sql)
|
135
|
+
@test = :sql
|
136
|
+
@sql = sql
|
137
|
+
end
|
138
|
+
|
139
|
+
def to_sql
|
140
|
+
[@sql]
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end # EZ
|
146
|
+
|
147
|
+
end # Caboose
|