lore 0.4.8 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +16 -7
- data/README.rdoc +91 -0
- data/benchmark/benchmark.sql +11 -0
- data/benchmark/results.txt +28 -0
- data/benchmark/select.rb +352 -0
- data/lib/lore.rb +22 -8
- data/lib/lore/adapters/context.rb +64 -0
- data/lib/lore/adapters/postgres-pr.rb +6 -0
- data/lib/lore/adapters/postgres-pr/connection.rb +93 -0
- data/lib/lore/adapters/postgres-pr/result.rb +63 -0
- data/lib/lore/{types.rb → adapters/postgres-pr/types.rb} +36 -0
- data/lib/lore/adapters/postgres.rb +24 -0
- data/lib/lore/adapters/postgres/connection.rb +81 -0
- data/lib/lore/adapters/postgres/result.rb +82 -0
- data/lib/lore/adapters/postgres/types.rb +91 -0
- data/lib/lore/bits.rb +18 -0
- data/lib/lore/cache/abstract_entity_cache.rb +2 -1
- data/lib/lore/cache/cacheable.rb +12 -177
- data/lib/lore/cache/memcache_entity_cache.rb +89 -0
- data/lib/lore/cache/memory_entity_cache.rb +77 -0
- data/lib/lore/cache/mmap_entity_cache.rb +2 -2
- data/lib/lore/cache/mmap_entity_cache_bork.rb +86 -0
- data/lib/lore/clause.rb +107 -35
- data/lib/lore/{exception → exceptions}/ambiguous_attribute.rb +2 -2
- data/lib/lore/{exception → exceptions}/cache_exception.rb +1 -1
- data/lib/lore/exceptions/database_exception.rb +16 -0
- data/lib/lore/{exception/invalid_parameter.rb → exceptions/invalid_field.rb} +7 -4
- data/lib/lore/exceptions/unknown_type.rb +18 -0
- data/lib/lore/exceptions/validation_failure.rb +71 -0
- data/lib/lore/gui/form_generator.rb +109 -60
- data/lib/lore/gui/lore_model_select_field.rb +1 -0
- data/lib/lore/migration.rb +84 -25
- data/lib/lore/model.rb +3 -18
- data/lib/lore/{aspect.rb → model/aspect.rb} +0 -0
- data/lib/lore/model/associations.rb +225 -0
- data/lib/lore/model/attribute_settings.rb +233 -0
- data/lib/lore/model/filters.rb +34 -0
- data/lib/lore/model/mockable.rb +62 -0
- data/lib/lore/{model_factory.rb → model/model_factory.rb} +68 -39
- data/lib/lore/model/model_instance.rb +382 -0
- data/lib/lore/{model_shortcuts.rb → model/model_shortcuts.rb} +7 -0
- data/lib/lore/model/polymorphic.rb +53 -0
- data/lib/lore/model/prepare.rb +97 -0
- data/lib/lore/model/table_accessor.rb +1016 -0
- data/lib/lore/query.rb +71 -0
- data/lib/lore/query_shortcuts.rb +43 -11
- data/lib/lore/strategies/table_delete.rb +115 -0
- data/lib/lore/strategies/table_insert.rb +146 -0
- data/lib/lore/strategies/table_select.rb +299 -0
- data/lib/lore/strategies/table_update.rb +155 -0
- data/lib/lore/validation/parameter_validator.rb +85 -26
- data/lib/lore/validation/type_validator.rb +34 -78
- data/{custom_models.rb → lore-0.9.2.gem} +0 -0
- data/lore.gemspec +26 -17
- data/spec/clause.rb +37 -0
- data/spec/fixtures/blank_models.rb +37 -0
- data/{test/model.rb → spec/fixtures/models.rb} +64 -41
- data/spec/fixtures/polymorphic_models.rb +68 -0
- data/spec/model_associations.rb +86 -0
- data/spec/model_create.rb +47 -0
- data/spec/model_definition.rb +151 -0
- data/spec/model_delete.rb +31 -0
- data/spec/model_inheritance.rb +50 -0
- data/spec/model_polymorphic.rb +85 -0
- data/spec/model_select.rb +101 -0
- data/spec/model_select_eager.rb +42 -0
- data/spec/model_union_select.rb +33 -0
- data/spec/model_update.rb +45 -0
- data/spec/model_validation.rb +20 -0
- data/spec/spec_db.sql +808 -0
- data/spec/spec_env.rb +19 -0
- data/spec/spec_helpers.rb +77 -0
- metadata +93 -82
- data/lib/lore/README.txt +0 -84
- data/lib/lore/behaviours/lockable.rb +0 -55
- data/lib/lore/behaviours/movable.rb +0 -72
- data/lib/lore/behaviours/paginated.rb +0 -31
- data/lib/lore/behaviours/versioned.rb +0 -36
- data/lib/lore/connection.rb +0 -152
- data/lib/lore/exception/invalid_klass_parameters.rb +0 -63
- data/lib/lore/exception/unknown_typecode.rb +0 -19
- data/lib/lore/result.rb +0 -119
- data/lib/lore/symbol.rb +0 -58
- data/lib/lore/table_accessor.rb +0 -1790
- data/lib/lore/table_deleter.rb +0 -116
- data/lib/lore/table_inserter.rb +0 -170
- data/lib/lore/table_instance.rb +0 -389
- data/lib/lore/table_selector.rb +0 -285
- data/lib/lore/table_updater.rb +0 -157
- data/lib/lore/validation.rb +0 -65
- data/lib/lore/validation/message.rb +0 -60
- data/lib/lore/validation/reason.rb +0 -52
- data/lore_test.log +0 -2366
- data/test/README +0 -31
- data/test/custom_models.rb +0 -18
- data/test/env.rb +0 -5
- data/test/prepare.rb +0 -37
- data/test/tc_aspect.rb +0 -58
- data/test/tc_cache.rb +0 -83
- data/test/tc_clause.rb +0 -104
- data/test/tc_deep_inheritance.rb +0 -49
- data/test/tc_factory.rb +0 -57
- data/test/tc_filter.rb +0 -37
- data/test/tc_form.rb +0 -32
- data/test/tc_model.rb +0 -140
- data/test/tc_prepare.rb +0 -44
- data/test/tc_refined_query.rb +0 -88
- data/test/tc_table_accessor.rb +0 -267
- data/test/tc_thread.rb +0 -100
- data/test/test_db.sql +0 -400
- data/test/test_lore.rb +0 -50
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
require 'lore'
|
3
|
+
require 'lore/cache/abstract_entity_cache'
|
4
|
+
begin
|
5
|
+
require 'mmap'
|
6
|
+
require 'activesupport'
|
7
|
+
require 'active_support/cache/memory_store'
|
8
|
+
rescue LoadError
|
9
|
+
Lore.log { 'Mmap or ActiveSupport for Ruby could not be loaded. You won\'t be able to use Mmap_Entity_Cache. ' }
|
10
|
+
module ActiveSupport
|
11
|
+
module Cache
|
12
|
+
class MemoryStore
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Lore
|
19
|
+
module Cache
|
20
|
+
|
21
|
+
|
22
|
+
# Implementation of entity cache using MMapped PStor files.
|
23
|
+
# Derived from Abstract_Entity_Cache.
|
24
|
+
# Uses Lore::Cache::Cache_Helpers for generating PStor files.
|
25
|
+
class Memory_Entity_Cache < Abstract_Entity_Cache
|
26
|
+
extend Cache_Helpers
|
27
|
+
|
28
|
+
@@logger = Logger.new('/tmp/lore_cache.log')
|
29
|
+
|
30
|
+
@@store = ActiveSupport::Cache::MemoryStore.new()
|
31
|
+
|
32
|
+
|
33
|
+
def self.flush(accessor)
|
34
|
+
index = accessor.table_name
|
35
|
+
return unless Lore.cache_enabled?
|
36
|
+
Dir.glob("/tmp/lore_cache__#{index}*").each { |cache_file|
|
37
|
+
@@logger.debug('Clearing cache file ' << cache_file.to_s.split('_').last)
|
38
|
+
begin
|
39
|
+
@@store.delete(cache_file.to_s.split('_').last)
|
40
|
+
rescue ::Exception => excep
|
41
|
+
# Another process already killed this file
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.read(accessor, query_obj)
|
47
|
+
@@logger.debug { 'Loading from cache: ' << index_for(query_obj[:query]) }
|
48
|
+
store = @@store.read("#{accessor.table_name}--#{index_for(query_obj[:query])}")
|
49
|
+
return [] unless store
|
50
|
+
|
51
|
+
return store[:values]
|
52
|
+
# result << accessor.new(r, joined_models, :cached)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.create(accessor, query_object, result)
|
56
|
+
entry = query_object.update({ :values => result })
|
57
|
+
@@store.write("#{accessor.table_name}--#{index_for(query_object[:query])}", entry)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.include?(accessor, query_obj)
|
61
|
+
hit = @@store.exist?("#{accessor.table_name}--#{index_for(query_obj[:query])}")
|
62
|
+
@@logger.debug { 'Cache miss: ' << index_for(query_obj[:query]) } unless hit
|
63
|
+
@@logger.info { 'CACHE MISS on ' << query_obj[:query] } unless hit
|
64
|
+
return hit
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.delete(index)
|
68
|
+
@@logger.debug { "Deleting index #{index}" }
|
69
|
+
@@store.delete(index)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
@@ -34,9 +34,9 @@ module Cache
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def self.read(accessor, query_obj)
|
37
|
-
|
37
|
+
Lore.log { 'Loading from cache: ' << index_for(query_obj[:query]) }
|
38
38
|
store = Mmap.new(storefile_of(accessor.table_name, query_obj[:query]))
|
39
|
-
|
39
|
+
Lore.log { 'STORE: ' << store.inspect }
|
40
40
|
return [] unless store
|
41
41
|
result = Marshal::load(store)
|
42
42
|
return result['dump']
|
@@ -0,0 +1,86 @@
|
|
1
|
+
|
2
|
+
require 'lore'
|
3
|
+
require 'lore/cache/abstract_entity_cache'
|
4
|
+
begin
|
5
|
+
require 'mmap'
|
6
|
+
rescue LoadError
|
7
|
+
Lore.log { 'Mmap for Ruby could not be found. You won\'t be able to use Mmap_Entity_Cache. ' }
|
8
|
+
end
|
9
|
+
|
10
|
+
module Lore
|
11
|
+
module Cache
|
12
|
+
|
13
|
+
|
14
|
+
# Implementation of entity cache using MMapped PStor files.
|
15
|
+
# Derived from Abstract_Entity_Cache.
|
16
|
+
# Uses Lore::Cache::Cache_Helpers for generating PStor files.
|
17
|
+
class Mmap_Entity_Cache < Abstract_Entity_Cache
|
18
|
+
extend Cache_Helpers
|
19
|
+
|
20
|
+
@@logger = Logger.new('/tmp/lore_cache.log')
|
21
|
+
|
22
|
+
|
23
|
+
def self.flush(accessor)
|
24
|
+
index = accessor.table_name
|
25
|
+
return unless Lore.cache_enabled?
|
26
|
+
Dir.glob("/tmp/lore_cache__#{index}*").each { |cache_file|
|
27
|
+
@@logger.debug('Clearing cache file ' << cache_file)
|
28
|
+
begin
|
29
|
+
File.unlink(cache_file)
|
30
|
+
rescue ::Exception => excep
|
31
|
+
# Another process already killed this file
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.read(accessor, query_obj)
|
37
|
+
@@logger.debug { 'Loading from cache: ' << index_for(query_obj[:query]) }
|
38
|
+
store = Mmap.new(storefile_of(accessor.table_name, query_obj[:query]))
|
39
|
+
return [] unless store
|
40
|
+
result = Marshal::load(store)
|
41
|
+
return result
|
42
|
+
|
43
|
+
vector = []
|
44
|
+
joined_models = result['dump'][:joined_models]
|
45
|
+
joined_models ||= []
|
46
|
+
result['dump'][:values].each { |r|
|
47
|
+
vector << accessor.new(r, joined_models, true)
|
48
|
+
}
|
49
|
+
return vector
|
50
|
+
|
51
|
+
#
|
52
|
+
# return result['dump']
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.create(accessor, query_object, result)
|
56
|
+
storefile = create_mmap(accessor, query_object, result)
|
57
|
+
end
|
58
|
+
def self.create_mmap(accessor, query_object, result)
|
59
|
+
storefile = storefile_of(accessor.table_name, query_object[:query])
|
60
|
+
store = create_store(storefile)
|
61
|
+
store.transaction do
|
62
|
+
# store['dump'] = { :values => result,
|
63
|
+
# :joined_models => query_object[:joined_models] }
|
64
|
+
store['dump'] = result
|
65
|
+
end
|
66
|
+
@@logger.debug('Creating cache entry for ' << storefile )
|
67
|
+
Mmap.new(storefile)
|
68
|
+
return storefile
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.include?(accessor, query_obj)
|
72
|
+
hit = FileTest.exist?(storefile_of(accessor.table_name, query_obj[:query]))
|
73
|
+
@@logger.debug { 'Cache miss: ' << index_for(query_obj[:query]) } unless hit
|
74
|
+
return hit
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.delete(index)
|
78
|
+
File.rm(storefile_of(accessor.table_name, query_obj[:query]))
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
data/lib/lore/clause.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
|
2
|
-
require('lore/table_accessor')
|
2
|
+
require('lore/model/table_accessor')
|
3
3
|
require('lore/query_shortcuts') # So far, a forward-declaration could do here as well
|
4
4
|
|
5
5
|
class String
|
6
|
+
# Poor man's SQL injection prevention ...
|
6
7
|
def lore_escape
|
7
8
|
self.gsub!("'","X")
|
8
9
|
self.gsub!('"','X')
|
@@ -10,6 +11,59 @@ class String
|
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
14
|
+
class Symbol
|
15
|
+
# {{{
|
16
|
+
|
17
|
+
# Do not overload Symbol#==
|
18
|
+
def eq(other)
|
19
|
+
Lore::Clause.new(self.to_s)==(other)
|
20
|
+
end
|
21
|
+
alias is eq
|
22
|
+
def >=(other)
|
23
|
+
Lore::Clause.new(self.to_s)>=(other)
|
24
|
+
end
|
25
|
+
def <=(other)
|
26
|
+
Lore::Clause.new(self.to_s)<=(other)
|
27
|
+
end
|
28
|
+
def >(other)
|
29
|
+
Lore::Clause.new(self.to_s)>(other)
|
30
|
+
end
|
31
|
+
def <(other)
|
32
|
+
Lore::Clause.new(self.to_s)<=(other)
|
33
|
+
end
|
34
|
+
def <=>(other)
|
35
|
+
Lore::Clause.new(self.to_s)<=>(other)
|
36
|
+
end
|
37
|
+
def not(other)
|
38
|
+
Lore::Clause.new(self.to_s)<=>(other)
|
39
|
+
end
|
40
|
+
def like(other)
|
41
|
+
Lore::Clause.new(self.to_s).like(other)
|
42
|
+
end
|
43
|
+
def ilike(other)
|
44
|
+
Lore::Clause.new(self.to_s).ilike(other)
|
45
|
+
end
|
46
|
+
def has_element(other)
|
47
|
+
Lore::Clause.new(self.to_s).has_element(other)
|
48
|
+
end
|
49
|
+
def has_element_like(other)
|
50
|
+
Lore::Clause.new(self.to_s).has_element_like(other)
|
51
|
+
end
|
52
|
+
def has_element_ilike(other)
|
53
|
+
Lore::Clause.new(self.to_s).has_element_ilike(other)
|
54
|
+
end
|
55
|
+
def in(other)
|
56
|
+
Lore::Clause.new(self.to_s).in(other)
|
57
|
+
end
|
58
|
+
def not_in(other)
|
59
|
+
Lore::Clause.new(self.to_s).not_in(other)
|
60
|
+
end
|
61
|
+
def between(s,e)
|
62
|
+
Lore::Clause.new(self.to_s).between(s,e)
|
63
|
+
end
|
64
|
+
|
65
|
+
end # }}}
|
66
|
+
|
13
67
|
module Lore
|
14
68
|
|
15
69
|
def self.parse_field_value(value)
|
@@ -32,7 +86,7 @@ module Lore
|
|
32
86
|
# k.limit(10)
|
33
87
|
# }
|
34
88
|
#
|
35
|
-
class Join
|
89
|
+
class Join
|
36
90
|
# {{{
|
37
91
|
|
38
92
|
def initialize(clause, base_klass, join_klass, type=:natural)
|
@@ -44,21 +98,10 @@ module Lore
|
|
44
98
|
# By joining with another klass, new attributes are added
|
45
99
|
# we have to include in the AS part of the query:
|
46
100
|
new_attributes = ''
|
47
|
-
# join_klass.get_attributes.each { |attr_set|
|
48
|
-
# table = attr_set[0]
|
49
|
-
# attr_set[1].each { |attrib_name|
|
50
|
-
# new_attributes += ', ' << "\n"
|
51
|
-
# new_attributes += table + '.' << attrib_name
|
52
|
-
# new_attributes += ' AS '
|
53
|
-
# new_attributes += '"' << table << '.' << attrib_name << '" '
|
54
|
-
# }
|
55
|
-
# }
|
56
101
|
implicit_joins = ''
|
57
102
|
@clause_parser.add_as(new_attributes)
|
58
103
|
|
59
|
-
@implicit_joins =
|
60
|
-
|
61
|
-
# @clause_parser.add_join(self)
|
104
|
+
@implicit_joins = Table_Select.build_joined_query(join_klass)
|
62
105
|
end
|
63
106
|
|
64
107
|
def implicit_joins
|
@@ -99,12 +142,26 @@ module Lore
|
|
99
142
|
|
100
143
|
end # class }}}
|
101
144
|
|
102
|
-
|
145
|
+
# Clause objects are responsible for operands on Model attributes,
|
146
|
+
# as in WHERE parts of a query.
|
147
|
+
# Model klass methods named like one if its (possibly inherited)
|
148
|
+
# field names return a preconfigured Clause object on that field.
|
149
|
+
#
|
150
|
+
# Example:
|
151
|
+
#
|
152
|
+
# (Car.num_seats > 100).to_sql --> "public.vehicle.num_seats > '100'"
|
153
|
+
#
|
154
|
+
class Clause < String
|
103
155
|
# {{{
|
104
156
|
|
105
157
|
attr_reader :field_name, :value_string, :left_side, :plan
|
106
158
|
|
107
159
|
def initialize(field_name='', left_side='', value_string='', plan={})
|
160
|
+
if field_name.instance_of?(TrueClass) then
|
161
|
+
return (Clause.new('1') == '1')
|
162
|
+
elsif field_name.instance_of?(FalseClass) then
|
163
|
+
return (Clause.new('1') == '0')
|
164
|
+
end
|
108
165
|
@value_string = value_string
|
109
166
|
@left_side = left_side
|
110
167
|
@field_name = field_name
|
@@ -114,31 +171,32 @@ module Lore
|
|
114
171
|
Clause_Parser.new(accessor)
|
115
172
|
end
|
116
173
|
|
174
|
+
# Check for Refined_Select needed for functionality of
|
175
|
+
# Refined_Select. Example:
|
176
|
+
#
|
177
|
+
# User.all.with(User.user_id.in( Admin.all(:user_id) ))
|
178
|
+
#
|
117
179
|
def not_in(nested_query_string)
|
118
|
-
# Check for Refined_Select needed for functionality of
|
119
|
-
# Refined_Select. Example:
|
120
|
-
#
|
121
|
-
# User.all.with(User.user_id.in( Admin.all(:user_id) ))
|
122
|
-
#
|
123
180
|
if(nested_query_string.instance_of? Refined_Select) then
|
124
181
|
nested_query_string.to_inner_select
|
125
182
|
else
|
183
|
+
nested_query_string = nested_query_string.join(',') if nested_query_string.is_a?(Array)
|
126
184
|
@value_string = @field_name << ' NOT IN (' << "\n" << nested_query_string.to_s << ') '
|
127
185
|
Clause.new(@value_string, @left_side+@value_string)
|
128
186
|
end
|
129
187
|
end
|
130
188
|
|
189
|
+
# Check for Refined_Select needed for functionality of
|
190
|
+
# Refined_Select. Example:
|
191
|
+
#
|
192
|
+
# User.all.with(User.user_id.in( Admin.all(:user_id) ))
|
193
|
+
#
|
131
194
|
def in(nested_query_string)
|
132
|
-
# Check for Refined_Select needed for functionality of
|
133
|
-
# Refined_Select. Example:
|
134
|
-
#
|
135
|
-
# User.all.with(User.user_id.in( Admin.all(:user_id) ))
|
136
|
-
#
|
137
195
|
if(nested_query_string.instance_of? Refined_Select) then
|
138
196
|
nested_query_string = nested_query_string.to_select
|
139
197
|
elsif nested_query_string.instance_of? Array then
|
140
|
-
raise ::Exception.new('IN field expects at least one element. ') if nested_query_string.length == 0
|
141
198
|
nested_query_string = nested_query_string.join(',')
|
199
|
+
nested_query_string = 'NULL' if nested_query_string.length == 0
|
142
200
|
elsif nested_query_string.instance_of? Range then
|
143
201
|
return between(nested_query_string.first, nested_query_string.last)
|
144
202
|
end
|
@@ -226,6 +284,7 @@ module Lore
|
|
226
284
|
end
|
227
285
|
Clause.new(@value_string, @left_side+@value_string, '', @plan)
|
228
286
|
end
|
287
|
+
alias is ==
|
229
288
|
|
230
289
|
def <=>(value)
|
231
290
|
if(value != :NULL)
|
@@ -235,19 +294,25 @@ module Lore
|
|
235
294
|
end
|
236
295
|
Clause.new(@field_name, @left_side+@value_string, '', @plan)
|
237
296
|
end
|
297
|
+
alias is_not <=>
|
298
|
+
|
238
299
|
def |(value)
|
239
|
-
|
240
|
-
Clause.new(
|
300
|
+
return Clause.new('1') == '1' if value.instance_of?(TrueClass)
|
301
|
+
return Clause.new('1') == '0' if value.instance_of?(FalseClass)
|
302
|
+
@value_string = " OR #{value.left_side}"
|
303
|
+
Clause.new(@value_string, "(#{@left_side+@value_string})", '', @plan)
|
241
304
|
end
|
242
305
|
alias or |
|
243
306
|
def &(value)
|
244
307
|
return unless value
|
308
|
+
return Clause.new('1') == '1' if value.instance_of?(TrueClass)
|
309
|
+
return Clause.new('1') == '0' if value.instance_of?(FalseClass)
|
245
310
|
if @left_side.gsub(' ','') != '' then
|
246
|
-
@value_string =
|
311
|
+
@value_string = " AND #{value.left_side}"
|
247
312
|
else
|
248
313
|
@value_string = value.left_side
|
249
314
|
end
|
250
|
-
Clause.new(@field_name, @left_side+@value_string, '', @plan)
|
315
|
+
Clause.new(@field_name, "(#{@left_side+@value_string})", '', @plan)
|
251
316
|
end
|
252
317
|
alias and &
|
253
318
|
|
@@ -306,8 +371,10 @@ module Lore
|
|
306
371
|
|
307
372
|
# parses / builds WHERE, GROUP BY, ORDER BY, LIMIT, ...
|
308
373
|
# part of the query:
|
309
|
-
class Clause_Parser
|
374
|
+
class Clause_Parser
|
310
375
|
# {{{
|
376
|
+
|
377
|
+
attr_reader :unions
|
311
378
|
|
312
379
|
def initialize(base_accessor)
|
313
380
|
|
@@ -322,6 +389,7 @@ module Lore
|
|
322
389
|
@clause[:filter] = ''
|
323
390
|
@clause[:where] = 't'
|
324
391
|
@clause[:joined] = []
|
392
|
+
@unions = false
|
325
393
|
@base_accessor = base_accessor
|
326
394
|
|
327
395
|
end # def
|
@@ -398,6 +466,7 @@ module Lore
|
|
398
466
|
clause << @clause[:group_by].to_s
|
399
467
|
clause << @clause[:order_by].to_s
|
400
468
|
clause << @clause[:limit].to_s
|
469
|
+
return clause
|
401
470
|
end # def
|
402
471
|
|
403
472
|
def where(where_clause)
|
@@ -408,7 +477,7 @@ module Lore
|
|
408
477
|
elsif where_clause.instance_of? FalseClass then
|
409
478
|
where_clause = '\'f\''
|
410
479
|
end
|
411
|
-
@clause[:where] =
|
480
|
+
@clause[:where] = "\nWHERE #{where_clause.to_s}"
|
412
481
|
return self
|
413
482
|
end # def
|
414
483
|
|
@@ -452,10 +521,15 @@ module Lore
|
|
452
521
|
return j
|
453
522
|
end
|
454
523
|
|
524
|
+
def union(select_query)
|
525
|
+
@unions ||= []
|
526
|
+
@unions << select_query
|
527
|
+
return self
|
528
|
+
end
|
529
|
+
|
455
530
|
def limit(limit_val, offset_val=0)
|
456
531
|
@clause[:limit] = ' LIMIT ' << limit_val.to_s
|
457
532
|
@clause[:offset] = ' OFFSET ' << offset_val.to_s
|
458
|
-
|
459
533
|
return self
|
460
534
|
end # def
|
461
535
|
|
@@ -473,7 +547,6 @@ module Lore
|
|
473
547
|
end
|
474
548
|
}
|
475
549
|
@clause[:group_by] = ' GROUP BY ' << absolute_field_names.join(',')
|
476
|
-
p @clause
|
477
550
|
return self
|
478
551
|
end # def
|
479
552
|
|
@@ -523,7 +596,6 @@ module Lore
|
|
523
596
|
end
|
524
597
|
|
525
598
|
def perform
|
526
|
-
|
527
599
|
end
|
528
600
|
|
529
601
|
# }}}
|