lore 0.4.8 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/Manifest.txt +16 -7
  2. data/README.rdoc +91 -0
  3. data/benchmark/benchmark.sql +11 -0
  4. data/benchmark/results.txt +28 -0
  5. data/benchmark/select.rb +352 -0
  6. data/lib/lore.rb +22 -8
  7. data/lib/lore/adapters/context.rb +64 -0
  8. data/lib/lore/adapters/postgres-pr.rb +6 -0
  9. data/lib/lore/adapters/postgres-pr/connection.rb +93 -0
  10. data/lib/lore/adapters/postgres-pr/result.rb +63 -0
  11. data/lib/lore/{types.rb → adapters/postgres-pr/types.rb} +36 -0
  12. data/lib/lore/adapters/postgres.rb +24 -0
  13. data/lib/lore/adapters/postgres/connection.rb +81 -0
  14. data/lib/lore/adapters/postgres/result.rb +82 -0
  15. data/lib/lore/adapters/postgres/types.rb +91 -0
  16. data/lib/lore/bits.rb +18 -0
  17. data/lib/lore/cache/abstract_entity_cache.rb +2 -1
  18. data/lib/lore/cache/cacheable.rb +12 -177
  19. data/lib/lore/cache/memcache_entity_cache.rb +89 -0
  20. data/lib/lore/cache/memory_entity_cache.rb +77 -0
  21. data/lib/lore/cache/mmap_entity_cache.rb +2 -2
  22. data/lib/lore/cache/mmap_entity_cache_bork.rb +86 -0
  23. data/lib/lore/clause.rb +107 -35
  24. data/lib/lore/{exception → exceptions}/ambiguous_attribute.rb +2 -2
  25. data/lib/lore/{exception → exceptions}/cache_exception.rb +1 -1
  26. data/lib/lore/exceptions/database_exception.rb +16 -0
  27. data/lib/lore/{exception/invalid_parameter.rb → exceptions/invalid_field.rb} +7 -4
  28. data/lib/lore/exceptions/unknown_type.rb +18 -0
  29. data/lib/lore/exceptions/validation_failure.rb +71 -0
  30. data/lib/lore/gui/form_generator.rb +109 -60
  31. data/lib/lore/gui/lore_model_select_field.rb +1 -0
  32. data/lib/lore/migration.rb +84 -25
  33. data/lib/lore/model.rb +3 -18
  34. data/lib/lore/{aspect.rb → model/aspect.rb} +0 -0
  35. data/lib/lore/model/associations.rb +225 -0
  36. data/lib/lore/model/attribute_settings.rb +233 -0
  37. data/lib/lore/model/filters.rb +34 -0
  38. data/lib/lore/model/mockable.rb +62 -0
  39. data/lib/lore/{model_factory.rb → model/model_factory.rb} +68 -39
  40. data/lib/lore/model/model_instance.rb +382 -0
  41. data/lib/lore/{model_shortcuts.rb → model/model_shortcuts.rb} +7 -0
  42. data/lib/lore/model/polymorphic.rb +53 -0
  43. data/lib/lore/model/prepare.rb +97 -0
  44. data/lib/lore/model/table_accessor.rb +1016 -0
  45. data/lib/lore/query.rb +71 -0
  46. data/lib/lore/query_shortcuts.rb +43 -11
  47. data/lib/lore/strategies/table_delete.rb +115 -0
  48. data/lib/lore/strategies/table_insert.rb +146 -0
  49. data/lib/lore/strategies/table_select.rb +299 -0
  50. data/lib/lore/strategies/table_update.rb +155 -0
  51. data/lib/lore/validation/parameter_validator.rb +85 -26
  52. data/lib/lore/validation/type_validator.rb +34 -78
  53. data/{custom_models.rb → lore-0.9.2.gem} +0 -0
  54. data/lore.gemspec +26 -17
  55. data/spec/clause.rb +37 -0
  56. data/spec/fixtures/blank_models.rb +37 -0
  57. data/{test/model.rb → spec/fixtures/models.rb} +64 -41
  58. data/spec/fixtures/polymorphic_models.rb +68 -0
  59. data/spec/model_associations.rb +86 -0
  60. data/spec/model_create.rb +47 -0
  61. data/spec/model_definition.rb +151 -0
  62. data/spec/model_delete.rb +31 -0
  63. data/spec/model_inheritance.rb +50 -0
  64. data/spec/model_polymorphic.rb +85 -0
  65. data/spec/model_select.rb +101 -0
  66. data/spec/model_select_eager.rb +42 -0
  67. data/spec/model_union_select.rb +33 -0
  68. data/spec/model_update.rb +45 -0
  69. data/spec/model_validation.rb +20 -0
  70. data/spec/spec_db.sql +808 -0
  71. data/spec/spec_env.rb +19 -0
  72. data/spec/spec_helpers.rb +77 -0
  73. metadata +93 -82
  74. data/lib/lore/README.txt +0 -84
  75. data/lib/lore/behaviours/lockable.rb +0 -55
  76. data/lib/lore/behaviours/movable.rb +0 -72
  77. data/lib/lore/behaviours/paginated.rb +0 -31
  78. data/lib/lore/behaviours/versioned.rb +0 -36
  79. data/lib/lore/connection.rb +0 -152
  80. data/lib/lore/exception/invalid_klass_parameters.rb +0 -63
  81. data/lib/lore/exception/unknown_typecode.rb +0 -19
  82. data/lib/lore/result.rb +0 -119
  83. data/lib/lore/symbol.rb +0 -58
  84. data/lib/lore/table_accessor.rb +0 -1790
  85. data/lib/lore/table_deleter.rb +0 -116
  86. data/lib/lore/table_inserter.rb +0 -170
  87. data/lib/lore/table_instance.rb +0 -389
  88. data/lib/lore/table_selector.rb +0 -285
  89. data/lib/lore/table_updater.rb +0 -157
  90. data/lib/lore/validation.rb +0 -65
  91. data/lib/lore/validation/message.rb +0 -60
  92. data/lib/lore/validation/reason.rb +0 -52
  93. data/lore_test.log +0 -2366
  94. data/test/README +0 -31
  95. data/test/custom_models.rb +0 -18
  96. data/test/env.rb +0 -5
  97. data/test/prepare.rb +0 -37
  98. data/test/tc_aspect.rb +0 -58
  99. data/test/tc_cache.rb +0 -83
  100. data/test/tc_clause.rb +0 -104
  101. data/test/tc_deep_inheritance.rb +0 -49
  102. data/test/tc_factory.rb +0 -57
  103. data/test/tc_filter.rb +0 -37
  104. data/test/tc_form.rb +0 -32
  105. data/test/tc_model.rb +0 -140
  106. data/test/tc_prepare.rb +0 -44
  107. data/test/tc_refined_query.rb +0 -88
  108. data/test/tc_table_accessor.rb +0 -267
  109. data/test/tc_thread.rb +0 -100
  110. data/test/test_db.sql +0 -400
  111. data/test/test_lore.rb +0 -50
@@ -0,0 +1,82 @@
1
+
2
+ require('postgres')
3
+
4
+ module Lore
5
+
6
+ class Result
7
+
8
+ attr_reader :query_hashval, :field_names, :field_types
9
+
10
+ # expects PGresult
11
+ def initialize(query, result)
12
+ @result = result
13
+ @field_types = nil
14
+ @result_rows = Array.new
15
+ @num_fields = @result.num_fields
16
+ @num_tuples = @result.num_tuples
17
+ @field_counter = 0
18
+ @field_names = @result.fields
19
+ end # def initialize
20
+
21
+ def get_field_value(row_index, field_name)
22
+ field_index = @result.fieldnum(field_name)
23
+ return @result.getvalue(row_index, field_index)
24
+ end # def get_field_value
25
+
26
+ def get_field_types()
27
+
28
+ return @field_types unless @field_types.nil?
29
+
30
+ @field_types = Hash.new
31
+ for field_index in 0...get_field_num()
32
+ @field_types[@result.fields[field_index]] = @result.type(field_index)
33
+ end
34
+
35
+ return @field_types
36
+ end
37
+ alias field_types get_field_types
38
+ alias get_field_names field_names
39
+
40
+ def get_field_num()
41
+ @num_fields
42
+ end
43
+
44
+ def get_tuple_num()
45
+ @num_tuples
46
+ end
47
+
48
+ def get_row(row_num=0)
49
+ @result[row_num]
50
+ end
51
+ def get_row_with_field_names(row_num=0)
52
+
53
+ return if @result.num_tuples == 0
54
+
55
+ row_result = Array.new
56
+
57
+ @field_counter = 0
58
+ for @field_counter in 0...@num_fields do
59
+ row_result << @result.getvalue(row_num, @field_counter)
60
+ end
61
+ @fieldnames = []
62
+ for @field_counter in 0...@num_fields do
63
+ @fieldnames << @result.fieldname(@field_counter)
64
+ end
65
+ return { :values => row_result, :fields => @fieldnames }
66
+ end
67
+
68
+ def get_rows()
69
+ rows = []
70
+ for tuple_count in 0...@result.num_tuples do
71
+ rows << @result[tuple_count]
72
+ end
73
+ return rows
74
+ end
75
+
76
+ def fieldname(index)
77
+ return @result.fieldname(index)
78
+ end
79
+
80
+ end # class Result
81
+
82
+ end # module Lore
@@ -0,0 +1,91 @@
1
+
2
+ module Lore
3
+
4
+ PG_BOOL = 16
5
+ PG_BYTEA = 17
6
+ PG_SMALLINT = 21
7
+ PG_CHAR = 18
8
+ PG_INT = 23
9
+ PG_TEXT = 25
10
+ PG_FLOAT = 701
11
+ PG_CHARACTER = 1042
12
+ PG_VARCHAR = 1043
13
+ PG_TIME = 1083
14
+ PG_TIMESTAMP_TIMEZONE = 1184
15
+ PG_TIMESTAMP = 1114
16
+ PG_DATE = 1082
17
+ PG_VCHAR_LIST = 1015
18
+ PG_DECIMAL = 1700
19
+
20
+ TYPE_NAMES = {
21
+ PG_BYTEA => 'bytea',
22
+ PG_BOOL => 'boolean',
23
+ PG_SMALLINT => 'small int',
24
+ PG_CHAR => 'char',
25
+ PG_INT => 'integer',
26
+ PG_TEXT => 'text',
27
+ PG_FLOAT => 'float',
28
+ PG_CHARACTER => 'character',
29
+ PG_VARCHAR => 'character varying(1000)',
30
+ PG_TIME => 'time',
31
+ PG_TIMESTAMP_TIMEZONE => 'timestamp with timezone',
32
+ PG_TIMESTAMP => 'timestamp',
33
+ PG_DATE => 'data',
34
+ PG_VCHAR_LIST => 'character varying[]',
35
+ PG_DECIMAL => 'decimal'
36
+ }
37
+
38
+ class Type
39
+
40
+ def self.integer; 'integer'; end
41
+ def self.boolean; 'boolean'; end
42
+ def self.bytea; 'bytea'; end
43
+ def self.char; 'char'; end
44
+ def self.varchar(length=255); 'character varying(' << length.to_s + ')'; end
45
+ def self.time; 'time'; end
46
+ def self.timestamp; 'timestamp'; end
47
+ def self.date; 'date'; end
48
+ def self.text; 'text'; end
49
+ def self.decimal; 'decimal'; end
50
+
51
+ def self.type_name(int)
52
+ TYPE_NAMES[int]
53
+ end
54
+
55
+ end
56
+
57
+ class Type_Filters
58
+
59
+ @@input_filters = {
60
+ PG_VCHAR_LIST => lambda { |v| "{#{v.join(',')}}" },
61
+ PG_BOOL => lambda { |v| if (v && v != 'f' || v == 't') then 'f' elsif (v.instance_of?(FalseClass) || v == 'f') then 'f' else nil end },
62
+ PG_DATE => lambda { |v| v.to_s },
63
+ PG_TIME => lambda { |v| v.to_s },
64
+ PG_TIMESTAMP_TIMEZONE => lambda { |v| v.to_s },
65
+ PG_TIMESTAMP => lambda { |v| v.to_s },
66
+ }
67
+ @@output_filters = {
68
+ PG_VCHAR_LIST => lambda { |v| v[1..-2].split(',') },
69
+ PG_INT => lambda { |v| if v then v.to_i else nil end },
70
+ PG_SMALLINT => lambda { |v| if v then v.to_i else nil end },
71
+ PG_FLOAT => lambda { |v| if v && v.length > 0 then v.to_f else nil end },
72
+ PG_DECIMAL => lambda { |v| if v && v.length > 0 then v.to_f else nil end },
73
+ PG_BOOL => lambda { |v| if v == 't' then true elsif v == 'f' then false else nil end }
74
+ # SLOW!
75
+ # PG_DATE => lambda { |v| Date.parse(v) if v },
76
+ # PG_TIME => lambda { |v| Time.parse(v) if v },
77
+ # PG_TIMESTAMP_TIMEZONE => lambda { |v| DateTime.parse(v) if v },
78
+ # PG_TIMESTAMP => lambda { |v| DateTime.parse(v) if v }
79
+ }
80
+
81
+ def self.in
82
+ @@input_filters
83
+ end
84
+
85
+ def self.out
86
+ @@output_filters
87
+ end
88
+
89
+ end
90
+
91
+ end # module
@@ -50,3 +50,21 @@ class Hash
50
50
  end
51
51
 
52
52
  end
53
+
54
+ class Object
55
+ def empty?
56
+ false
57
+ end
58
+ end
59
+
60
+ class String
61
+ def empty?
62
+ return self == ''
63
+ end
64
+ end
65
+
66
+ class Nil
67
+ def empty?
68
+ true
69
+ end
70
+ end
@@ -2,6 +2,7 @@
2
2
  require('logger')
3
3
  require('pstore')
4
4
  require('digest/md5')
5
+ require('lore/exceptions/cache_exception')
5
6
 
6
7
  module Lore
7
8
  module Cache
@@ -69,7 +70,7 @@ module Cache
69
70
  end
70
71
 
71
72
  def storefile_of(model_name, query)
72
- '/tmp/lore_cache__' << model_name + '_' << Digest::MD5.hexdigest(query)
73
+ "/tmp/lore_cache__#{model_name}_#{Digest::MD5.hexdigest(query)}"
73
74
  end
74
75
 
75
76
  def create_store(storefile_name)
@@ -2,198 +2,33 @@
2
2
  require('pstore')
3
3
 
4
4
  require('lore/cache/bits')
5
- require('lore/exception/cache_exception')
5
+ require('lore/exceptions/cache_exception')
6
6
 
7
7
  module Lore
8
8
  module Cache
9
9
 
10
10
  module Cacheable
11
11
 
12
- @@cache_control = Hash.new
13
- @@cache_on_pkeys = Hash.new
14
- @@distinctive_params = Hash.new
15
- @@cache_entities = false
12
+ @@cache_impl = false
16
13
 
17
- @@logger = Lore.logger
18
-
19
- public
20
-
21
- def is_cacheable(controller)
22
- controller = controller.intern unless controller.instance_of? Symbol
23
- return !@@cache_control[controller].nil? && @@cache_control[controller]
14
+ def entity_cache
15
+ @@cache_impl
24
16
  end
25
17
 
26
- # Returns whether a Table_Accessor state
27
- # is cached or not.
28
- def is_cached(controller, mode, keys)
29
-
30
- store_filename = Lore::Cache::store_name(self.name.to_s, controller, mode, keys)
31
- if store_filename.nil? then
32
- excep_mesg = 'Cannot resolve store filename for ['+self.name.to_s+'|'+controller+'|'+mode+']'
33
- excep_mesg += ' Keys: '+keys.to_s
34
- raise ::Exception.new(excep_mesg)
35
- end
36
-
37
- if FileTest.exist?(store_filename) then
38
- true
39
- else
40
- false
41
- end
42
-
18
+ def use_entity_cache(entity_cache_class)
19
+ @@cache_impl = entity_cache_class
43
20
  end
44
21
 
45
- protected
46
-
47
- def cache(controller, additional_params=nil)
48
- controller = controller.intern unless controller.instance_of? Symbol
49
- @@cache_control[controller] = true
50
- if !additional_params.nil? then
51
- @@distinctive_params[controller] = Array.new unless !@@distinctive_params[controller].nil?
52
- @@distinctive_params[controller] += additional_params
53
- end
54
-
22
+ def flush_entity_cache
23
+ @@cache_impl.flush(self) if @@cache_impl
55
24
  end
56
25
 
57
- def cache_select_entities
58
- @@cache_entities = true
26
+ def create_entity_cache(query_string, result)
27
+ @@cache_impl.create(self, query_string, result) if @@cache_impl
59
28
  end
60
29
 
61
- def cache_entities
62
- @@cache_entities
63
- end
64
-
65
- def cache_on_pkeys(controller, additional_params=nil)
66
- @@logger.log('Caching depending from pkeys only, controller: ' << controller.to_s << ', params: ' << additional_params.to_s)
67
- cache(controller, additional_params)
68
- @@cache_on_pkeys[controller] = true
69
- end
70
-
71
- def cache_pkeys_only?(controller)
72
- !@@cache_on_pkeys[controller].nil? && @@cache_on_pkeys[controller]
73
- end
74
-
75
- def nocache(controller)
76
- controller = controller.intern unless controller.instance_of? Symbol
77
- @@cache_control[controller] = false
78
- end
79
-
80
- public
81
-
82
- def cache_params(controller)
83
- return @@distinctive_params[controller] unless @@distinctive_params[controller].nil?
84
- return Hash.new
85
- end
86
-
87
- def create_cache(controller, mode, keys, output)
88
-
89
- @@logger.log('cache_on_pkeys: '+@@cache_on_pkeys.inspect)
90
- if !@@cache_on_pkeys[controller.intern].nil? &&
91
- @@cache_on_pkeys[controller.intern]
92
- then
93
- keys = filter_pkeys(self, controller, keys)
94
- @@logger.log('Filtered keys: '+keys.inspect)
95
- end
96
-
97
- if !@@cache_control[controller.intern].nil? &&
98
- @@cache_control[controller.intern]
99
- then
100
-
101
- if !@@cache_control[:self].nil? &&
102
- @@cache_control[:self]
103
- then
104
- class_copy = self
105
- end
106
-
107
- if is_cached(controller, mode, keys) then
108
- return
109
- end
110
-
111
- store = create_store(controller, mode, keys, output)
112
- store.transaction do
113
- store['klass'] = class_copy unless class_copy.nil?
114
- store['controller'] = controller
115
- store['mode'] = mode
116
- store['keys'] = keys
117
- store['output'] = output
118
- end
119
-
120
- else
121
- raise Cache_Write_Exception.new(self.name.to_s, controller, mode, keys)
122
- end
123
-
124
- end
125
-
126
- public
127
-
128
- def create_cache!(controller, mode, keys, output)
129
- invalidate(controller, mode, keys)
130
- create_cache(controller, mode, keys)
131
- end
132
-
133
- public
134
-
135
- def read_cache(controller, mode, keys)
136
-
137
- if !@@cache_control[controller.intern].nil? &&
138
- @@cache_control[controller.intern] &&
139
- is_cached(controller, mode, keys)
140
- then
141
-
142
- output = ''
143
- store = create_store(controller, mode, keys, output)
144
- store.transaction do
145
- output = store['output']
146
- end
147
-
148
- return output
149
-
150
- else
151
- raise Cache_Read_Exception.new(self.name.to_s, controller, mode, keys)
152
- end
153
-
154
- end
155
-
156
- def invalidate(controller, mode, keys)
157
- File.unlink(Lore::Cache::store_name(self.name.to_s, controller, mode, keys))
158
- end
159
- def invalidate_all(controller=:all, mode=:all, keys=:all)
160
- cwd = Dir.getwd
161
- Dir.chdir('/tmp/')
162
- if keys == :all then keys = Hash.new end
163
- invalid_cache_pattern = Lore::Cache::store_name(self.name.to_s, controller, mode, keys)
164
- @@logger.log('Deleting cache for: '+invalid_cache_pattern)
165
- invalid_cache_files = Dir.glob(invalid_cache_pattern)
166
- invalid_cache_files.each { |cache_file|
167
- @@logger.log('Deleting '+cache_file)
168
- File.delete(cache_file)
169
- }
170
- Dir.chdir(cwd)
171
- end
172
-
173
- private
174
-
175
- def create_store(controller, mode, keys, output)
176
- store_file = Lore::Cache::store_name(self.to_s, controller, mode, keys)
177
- store = PStore.new(store_file) unless store_file.nil?
178
- return store
179
- end
180
-
181
- def filter_pkeys(model_klass, controller, keys)
182
- if !model_klass.respond_to? 'key_array' then
183
- @@logger.log('could not filter for pkeys')
184
- return keys
185
- end
186
- result = Hash.new
187
- model_klass.key_array.each { |pkey|
188
- result[pkey] = keys[pkey]
189
- }
190
- if !@@distinctive_params[controller].nil? then
191
- @@distinctive_params[controller].each { |param|
192
- result[param] = keys[param]
193
- }
194
- end
195
-
196
- return result
30
+ def read_entity_cache(query_string)
31
+ @@cache_impl.read(self, query_string) if @@cache_impl
197
32
  end
198
33
 
199
34
  end
@@ -0,0 +1,89 @@
1
+
2
+ require 'lore'
3
+ require 'lore/cache/abstract_entity_cache'
4
+ begin
5
+ require 'mmap'
6
+ require 'activesupport'
7
+ require 'active_support/cache/mem_cache_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
+
11
+ module ActiveSupport
12
+ module Cache
13
+ class MemCacheStore
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ class Object
20
+ def logger
21
+ Lore.logger
22
+ end
23
+ end
24
+
25
+ module Lore
26
+ module Cache
27
+
28
+
29
+ # Implementation of entity cache using MMapped PStor files.
30
+ # Derived from Abstract_Entity_Cache.
31
+ # Uses Lore::Cache::Cache_Helpers for generating PStor files.
32
+ class Memcache_Entity_Cache < Abstract_Entity_Cache
33
+ extend Cache_Helpers
34
+
35
+ @@logger = Logger.new('/tmp/lore_cache.log')
36
+
37
+ @@store = ActiveSupport::Cache::MemCacheStore.new()
38
+
39
+
40
+ def self.flush(accessor)
41
+ index = accessor.table_name
42
+ return unless Lore.cache_enabled?
43
+ Dir.glob("/tmp/lore_cache__#{index}*").each { |cache_file|
44
+ @@logger.debug('Clearing cache file ' << cache_file.to_s.split('_').last)
45
+ begin
46
+ @@store.delete(cache_file.to_s.split('_').last)
47
+ rescue ::Exception => excep
48
+ # Another process already killed this file
49
+ end
50
+ }
51
+ end
52
+
53
+ def self.read(accessor, query_obj)
54
+ @@logger.debug { 'Loading from cache: ' << index_for(query_obj[:query]) }
55
+ store = @@store.read("#{accessor.table_name}--#{index_for(query_obj[:query])}")
56
+ return [] unless store
57
+ return store[:values]
58
+
59
+ result = []
60
+ store.each { |r|
61
+ joined_models = r[:joined_models]
62
+ joined_models ||= []
63
+ result << accessor.new(r[:values].dup, joined_models, true)
64
+ }
65
+ return result
66
+ end
67
+
68
+ def self.create(accessor, query_object, result)
69
+ entry = query_object.update({ :values => result })
70
+ @@store.write("#{accessor.table_name}--#{index_for(query_object[:query])}", entry)
71
+ end
72
+
73
+ def self.include?(accessor, query_obj)
74
+ hit = @@store.exist?("#{accessor.table_name}--#{index_for(query_obj[:query])}")
75
+ @@logger.debug { 'Cache miss: ' << index_for(query_obj[:query]) } unless hit
76
+ return hit
77
+ end
78
+
79
+ def self.delete(index)
80
+ @@logger.debug { "Deleting index #{index}" }
81
+ @@store.delete(index)
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
88
+
89
+