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.
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
+