lore 0.4.8 → 0.9.2
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/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
|
# }}}
|