lore 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README +74 -0
- data/aspect.rb +80 -0
- data/behaviours/lockable.rb +41 -0
- data/behaviours/movable.rb +54 -0
- data/behaviours/versioned.rb +24 -0
- data/benchmark.rb +193 -0
- data/bits.rb +52 -0
- data/cache/abstract_entity_cache.rb +82 -0
- data/cache/bits.rb +22 -0
- data/cache/cacheable.rb +202 -0
- data/cache/cached_entities.rb +116 -0
- data/cache/file_index.rb +35 -0
- data/cache/mmap_entity_cache.rb +67 -0
- data/clause.rb +528 -0
- data/connection.rb +155 -0
- data/custom_functions.sql +14 -0
- data/exception/ambiguous_attribute.rb +14 -0
- data/exception/cache_exception.rb +30 -0
- data/exception/invalid_klass_parameters.rb +63 -0
- data/exception/invalid_parameter.rb +42 -0
- data/exception/unknown_typecode.rb +19 -0
- data/file_index.sql +56 -0
- data/gui/erb_template.rb +79 -0
- data/gui/erb_template_helpers.rhtml +19 -0
- data/gui/form.rb +314 -0
- data/gui/form_element.rb +676 -0
- data/gui/form_generator.rb +151 -0
- data/gui/templates/button.rhtml +2 -0
- data/gui/templates/checkbox.rhtml +3 -0
- data/gui/templates/checkbox_row.rhtml +1 -0
- data/gui/templates/file.rhtml +2 -0
- data/gui/templates/file_readonly.rhtml +3 -0
- data/gui/templates/form_element.rhtml +5 -0
- data/gui/templates/form_element_horizontal.rhtml +3 -0
- data/gui/templates/form_element_listed.rhtml +8 -0
- data/gui/templates/form_table.rhtml +3 -0
- data/gui/templates/form_table_blank.rhtml +3 -0
- data/gui/templates/form_table_horizontal.rhtml +8 -0
- data/gui/templates/password.rhtml +2 -0
- data/gui/templates/password_readonly.rhtml +3 -0
- data/gui/templates/radio.rhtml +1 -0
- data/gui/templates/radio_row.rhtml +1 -0
- data/gui/templates/select.rhtml +23 -0
- data/gui/templates/text.rhtml +2 -0
- data/gui/templates/text_readonly.rhtml +3 -0
- data/gui/templates/textarea.rhtml +3 -0
- data/gui/templates/textarea_readonly.rhtml +4 -0
- data/lore.gemspec +40 -0
- data/lore.rb +94 -0
- data/migration.rb +48 -0
- data/model.rb +139 -0
- data/model_factory.rb +202 -0
- data/model_shortcuts.rb +16 -0
- data/query_shortcuts.rb +367 -0
- data/reserved_methods.txt +3 -0
- data/result.rb +100 -0
- data/symbol.rb +58 -0
- data/table_accessor.rb +1926 -0
- data/table_deleter.rb +115 -0
- data/table_inserter.rb +168 -0
- data/table_instance.rb +384 -0
- data/table_selector.rb +314 -0
- data/table_updater.rb +155 -0
- data/test/README +31 -0
- data/test/env.rb +5 -0
- data/test/lore_test.log +8218 -0
- data/test/model.rb +142 -0
- data/test/prepare.rb +37 -0
- data/test/tc_aspect.rb +58 -0
- data/test/tc_cache.rb +80 -0
- data/test/tc_clause.rb +104 -0
- data/test/tc_deep_inheritance.rb +49 -0
- data/test/tc_factory.rb +57 -0
- data/test/tc_filter.rb +37 -0
- data/test/tc_form.rb +32 -0
- data/test/tc_model.rb +86 -0
- data/test/tc_prepare.rb +45 -0
- data/test/tc_refined_query.rb +88 -0
- data/test/tc_table_accessor.rb +265 -0
- data/test/test.log +181 -0
- data/test/test_db.sql +400 -0
- data/test/ts_lore.rb +49 -0
- data/types.rb +55 -0
- data/validation/message.rb +60 -0
- data/validation/parameter_validator.rb +104 -0
- data/validation/reason.rb +54 -0
- data/validation/type_validator.rb +91 -0
- data/validation.rb +65 -0
- metadata +170 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2005-2008 Tobias Fuchs (fuchs@wortundform.de)
|
3
|
+
Lore has evolved from Cuba::DB, Copyright (c) 2004 Tobias Fuchs (fuchs@wortundform.de)
|
4
|
+
|
5
|
+
(MIT Licence)
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
8
|
+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
9
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
10
|
+
to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
13
|
+
the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
16
|
+
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
18
|
+
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
19
|
+
DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
Q: Are there adapters for other DBMS than PostgreSQL?
|
3
|
+
A: PostgreSQL is - to me and everyone using Lore i know of -
|
4
|
+
the best (free) choice for sophisticated database-driven
|
5
|
+
apps, so frankly said: Implementing adapters is not on my
|
6
|
+
top priority list. Adapters also come with some cost in
|
7
|
+
performance, by introducing an additional level of
|
8
|
+
abstraction.
|
9
|
+
So far, Lore supports PostgreSQL (7.4 to 8.3 tested).
|
10
|
+
Adapters are planned for oracle mysql and sqlite though.
|
11
|
+
Lore is SQL 92 compatible and not using any exotic
|
12
|
+
Postgres features, so it *might* work fine on e.g. mysql
|
13
|
+
and sqlite with just minor modifications. It is caring
|
14
|
+
about cascading dependencies etc, so you don't even need
|
15
|
+
foreign keys or table inheritance.
|
16
|
+
You will have to change the code establishing DB
|
17
|
+
connections (see connection.rb), but as those are
|
18
|
+
abstracted, just a couple of lines of code in connection.rb
|
19
|
+
had to be changed.
|
20
|
+
|
21
|
+
Q: How do i establish a DB connection at all?
|
22
|
+
A: In /lore.rb, edit the Hash instance @logins. Lore is caring
|
23
|
+
about connections itself.
|
24
|
+
You have to enter a so-called context (database, that is),
|
25
|
+
though:
|
26
|
+
|
27
|
+
Lore::Context.enter :my_dbname
|
28
|
+
|
29
|
+
Q: What about connection pooling?
|
30
|
+
A: Use PGPool for that, it's way better than any
|
31
|
+
implementation of connection pooling in ruby.
|
32
|
+
Install PGPool and tell lore to connect to PGPool's port
|
33
|
+
instead of PostgreSQL's one.
|
34
|
+
Example:
|
35
|
+
|
36
|
+
Lore.server = 'localhost:9999'
|
37
|
+
|
38
|
+
That's it!
|
39
|
+
|
40
|
+
Q: Which solution do you recommend for query result caching?
|
41
|
+
A: There are many possibilities: Tempfiles, MMap and Memcached
|
42
|
+
are the most widespread solutions known to me.
|
43
|
+
Many will agree that MMap is the best solution in matters
|
44
|
+
of performance on a single server, and memcached as soon as
|
45
|
+
more than one server is involved in your application.
|
46
|
+
|
47
|
+
Q: There are failing unit tests!!
|
48
|
+
A: I know. Those document missing features i'm implementing
|
49
|
+
at the very moment :) Be sure to update once in a while!
|
50
|
+
|
51
|
+
Q: After creating a model via Model_Factory: How do i change
|
52
|
+
it?
|
53
|
+
A: At the moment, this is not implemented, but tracked as
|
54
|
+
'missing feature'. This will be possible in version 1.0 at
|
55
|
+
the latest.
|
56
|
+
|
57
|
+
Q: There's so much documentation missing, the API isn't really
|
58
|
+
helpful. Where can i get more info and examples?
|
59
|
+
A: Have a look at the wiki. I'm updating it quite often, and
|
60
|
+
new features are explained in the wiki first:
|
61
|
+
http://infranode.com/wiki/
|
62
|
+
|
63
|
+
Q: I found a bug!! What now?
|
64
|
+
A: In case you're registered at rubyforge, visit
|
65
|
+
http://lore.rubyforge.org and submit a bug report.
|
66
|
+
Otherwise, just send me a mail (fuchs@atomnode.net).
|
67
|
+
If you're using Lore and are your progress is blocked by a
|
68
|
+
bug, i'll fix it almost instantly. I'm checking my mails
|
69
|
+
more often than the tickets at rubyforge, though.
|
70
|
+
|
71
|
+
Q: I want to join the dev team.
|
72
|
+
A: Great! Just send a mail to fuchs@wortundform.de, i will add
|
73
|
+
you on Rubyforge if you got some code to share.
|
74
|
+
|
data/aspect.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Lore
|
4
|
+
|
5
|
+
# Steps taken in .create are:
|
6
|
+
#
|
7
|
+
# * .before_create(attribs)
|
8
|
+
# * applying input filters
|
9
|
+
# * .before_create_after_filters(attribs)
|
10
|
+
# * distributing attributes to tables:
|
11
|
+
# { :attrib => 'value' } -> { 'public.my_table' => { :attrib => 'value'} }
|
12
|
+
# * .before_create_and_validation(table_attribs)
|
13
|
+
# * validation
|
14
|
+
# * .before_insert(table_attribs)
|
15
|
+
# * insert operation
|
16
|
+
# * .before_load(attribs)
|
17
|
+
# * load new instance from DB (thus getting final table values)
|
18
|
+
# * .after_create(created_instance)
|
19
|
+
|
20
|
+
module Aspect
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def before_delete(args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def after_delete(args)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Expects arguments (value hash) passed
|
31
|
+
# to Table_Accessor.create
|
32
|
+
def before_create(args)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Expects arguments (value hash) passed
|
36
|
+
# to Table_Accessor.create
|
37
|
+
def after_filters(filtered_args)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Expects arguments (value hash) passed
|
41
|
+
# to Table_Accessor.create and distributed
|
42
|
+
# to table names
|
43
|
+
def before_validation(distributed_args)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Expects arguments (value hash) passed
|
47
|
+
# to Table_Accessor.create and distributed
|
48
|
+
# to table names
|
49
|
+
def before_insert(validated_args)
|
50
|
+
end
|
51
|
+
|
52
|
+
def before_load(completed_args)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Expects object that has been created.
|
56
|
+
def after_create(obj)
|
57
|
+
end
|
58
|
+
|
59
|
+
public
|
60
|
+
|
61
|
+
# Expects model instance to be deleted.
|
62
|
+
def before_instance_delete(model_instance)
|
63
|
+
end
|
64
|
+
# Expects model instance table rows have been
|
65
|
+
# deleted of
|
66
|
+
def after_instance_delete(model_instance)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Expects arguments (value hash) passed
|
70
|
+
# to Table_Accessor.create and object
|
71
|
+
# to be updated.
|
72
|
+
def before_commit(obj)
|
73
|
+
end
|
74
|
+
# Expects updated object.
|
75
|
+
def after_commit(obj)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module Lore
|
3
|
+
module Behaviours
|
4
|
+
|
5
|
+
module Lockable
|
6
|
+
|
7
|
+
# Defines which attribute to use for locking.
|
8
|
+
# Default is 'lock'.
|
9
|
+
# Usage:
|
10
|
+
#
|
11
|
+
# If a block is given, a lock is only set when it
|
12
|
+
# evaluates to true:
|
13
|
+
#
|
14
|
+
# lock_by(:lock) { |me|
|
15
|
+
# me.user_id != $user.user_id
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
def lock_by(attrib, &block)
|
19
|
+
@lock_proc = block
|
20
|
+
@lock_attr = attrib
|
21
|
+
@lock_attr_name = attrib.to_s.split('.')[-1].intern
|
22
|
+
end
|
23
|
+
|
24
|
+
def lock!(inst)
|
25
|
+
inst[@lock_attr] = true
|
26
|
+
commit
|
27
|
+
end # def
|
28
|
+
|
29
|
+
def release!(inst)
|
30
|
+
inst[@lock_attr] = false
|
31
|
+
commit
|
32
|
+
end # def
|
33
|
+
|
34
|
+
def locked?(inst)
|
35
|
+
(inst[@lock_attr] == true) || (inst[@lock_attr] == 't')
|
36
|
+
end # def
|
37
|
+
|
38
|
+
end # module
|
39
|
+
|
40
|
+
end # module
|
41
|
+
end # module
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
module Lore
|
3
|
+
module Behaviours
|
4
|
+
|
5
|
+
module Movable
|
6
|
+
|
7
|
+
def ordered_by(attr)
|
8
|
+
@order_attr = attr
|
9
|
+
@order_attr_name = attr.to_s.split('.')[-1].intern
|
10
|
+
end
|
11
|
+
|
12
|
+
def move(inst, sortpos, criteria)
|
13
|
+
|
14
|
+
sortpos = sortpos.to_i
|
15
|
+
return if sortpos < 1
|
16
|
+
|
17
|
+
sortpos_old = inst.attr[@order_attr_name].to_i
|
18
|
+
|
19
|
+
# move down:
|
20
|
+
if sortpos.to_i > sortpos_old then
|
21
|
+
|
22
|
+
self.update { |na|
|
23
|
+
na.set({@order_attr_name => @order_attr-1}).where(
|
24
|
+
(criteria) &
|
25
|
+
(@order_attr <= sortpos) &
|
26
|
+
(@order_attr > sortpos_old)
|
27
|
+
)
|
28
|
+
}
|
29
|
+
|
30
|
+
elsif sortpos.to_i < sortpos_old then
|
31
|
+
|
32
|
+
self.update { |na|
|
33
|
+
na.set({@order_attr_name => @order_attr+1}).where(
|
34
|
+
(criteria) &
|
35
|
+
(@order_attr >= sortpos) &
|
36
|
+
(@order_attr < sortpos_old)
|
37
|
+
)
|
38
|
+
}
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
if sortpos != sortpos_old then
|
43
|
+
|
44
|
+
inst.set_attribute_value(@order_attr_name, sortpos)
|
45
|
+
inst.commit()
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end # def
|
50
|
+
|
51
|
+
end # module
|
52
|
+
|
53
|
+
end # module
|
54
|
+
end # module
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module Lore
|
3
|
+
module Behaviours
|
4
|
+
|
5
|
+
module Versioned
|
6
|
+
|
7
|
+
# Defines attribute the version number is stored in.
|
8
|
+
# Default is 'version'.
|
9
|
+
def version_by(attrib)
|
10
|
+
@version_attr = attrib
|
11
|
+
@version_attr_name = attrib.to_s.split('.')[-1].intern
|
12
|
+
end
|
13
|
+
|
14
|
+
# Overloads commit so it increments the version attribute
|
15
|
+
# before saving instance to database.
|
16
|
+
def commit()
|
17
|
+
set_attribute_value(@version_attr, self.attr[@version_attr].to_i + 1)
|
18
|
+
super()
|
19
|
+
end
|
20
|
+
|
21
|
+
end # module
|
22
|
+
|
23
|
+
end # module
|
24
|
+
end # module
|
data/benchmark.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
|
2
|
+
require('lore/model')
|
3
|
+
require('lore/cache/mmap_entity_cache')
|
4
|
+
|
5
|
+
Lore::Model.use_entity_cache Lore::Cache::MMap_Entity_Cache
|
6
|
+
# Aurita.load_project :default
|
7
|
+
# Aurita.import_plugin_model :wiki, :article
|
8
|
+
|
9
|
+
# include Aurita::Plugins::Wiki
|
10
|
+
|
11
|
+
require('benchmark')
|
12
|
+
include Benchmark
|
13
|
+
# require('profile')
|
14
|
+
Lore.logfile = './benchmark_query.log'
|
15
|
+
|
16
|
+
require('rubygems')
|
17
|
+
require('activerecord')
|
18
|
+
|
19
|
+
ActiveRecord::Base.establish_connection(
|
20
|
+
:adapter => 'postgresql',
|
21
|
+
:host => 'localhost',
|
22
|
+
:username => 'cuba',
|
23
|
+
:password => 'cuba23',
|
24
|
+
:database => 'artest'
|
25
|
+
)
|
26
|
+
|
27
|
+
Lore.add_login_data('artest' => ['cuba', 'cuba23'])
|
28
|
+
Lore::Context.enter :artest
|
29
|
+
|
30
|
+
module AR
|
31
|
+
class Content < ActiveRecord::Base
|
32
|
+
end
|
33
|
+
|
34
|
+
class Article < Content
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module Lore
|
39
|
+
class Content < Lore::Model
|
40
|
+
table :contents, :public
|
41
|
+
primary_key :id, :content_id_seq
|
42
|
+
end
|
43
|
+
|
44
|
+
class Article < Content
|
45
|
+
table :articles, :public
|
46
|
+
primary_key :id, :article_id_seq
|
47
|
+
is_a Content, :content_id
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
num_loops = 10000
|
52
|
+
|
53
|
+
class Result_Parser
|
54
|
+
require 'inline'
|
55
|
+
|
56
|
+
def initialize(result) # expects PGresult
|
57
|
+
|
58
|
+
@result = result
|
59
|
+
@field_types = nil
|
60
|
+
@result_rows = Array.new
|
61
|
+
@num_fields = @result.num_fields
|
62
|
+
@field_name_split = Array.new
|
63
|
+
@field_name = String.new
|
64
|
+
@table_name = String.new
|
65
|
+
@field_counter = 0
|
66
|
+
|
67
|
+
end # def initialize
|
68
|
+
|
69
|
+
=begin
|
70
|
+
inline { |builder|
|
71
|
+
builder.c <<-EOC
|
72
|
+
VALUE get_row_c(VALUE result, int row_num) {
|
73
|
+
unsigned int field_counter;
|
74
|
+
VALUE row_result, field_name, table_name, field_name_split;
|
75
|
+
VALUE value_set = rb_hash_new();
|
76
|
+
field_name = rb_string_new();
|
77
|
+
table_name = rb_string_new();
|
78
|
+
row_result = rb_hash_new();
|
79
|
+
for(field_counter = 0; field_counter < num_fields; field_counter++) {
|
80
|
+
value_set = result->getvalue(row_num, UINT2NUM(field_counter));
|
81
|
+
// if(row_result[@table_name].nil?) then
|
82
|
+
if(rb_hash_aref(row_result, table_name) == QNil) {
|
83
|
+
// row_result[@table_name] = Hash.new()
|
84
|
+
rb_hash_aset(row_result, table_name, value_set)
|
85
|
+
}
|
86
|
+
rb_hash_aset(rb_hash_aref)row_result, table_name), value_set);
|
87
|
+
}
|
88
|
+
return row_result;
|
89
|
+
}
|
90
|
+
EOC
|
91
|
+
}
|
92
|
+
=end
|
93
|
+
|
94
|
+
def get_row(row_num=0)
|
95
|
+
|
96
|
+
return if @result.num_tuples == 0
|
97
|
+
|
98
|
+
row_result = Hash.new
|
99
|
+
|
100
|
+
@field_name_split = Array.new
|
101
|
+
@field_name = String.new
|
102
|
+
@table_name = String.new
|
103
|
+
@field_counter = 0
|
104
|
+
for @field_counter in 0...@num_fields do
|
105
|
+
# admin.rd__content.name -> [admin, rd__content, name]
|
106
|
+
@field_name = @result.fieldname(@field_counter)
|
107
|
+
|
108
|
+
row_result[@field_name] = @result.getvalue(row_num,@field_counter)
|
109
|
+
end
|
110
|
+
|
111
|
+
@result_rows[row_num] = row_result
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
def get_rows()
|
116
|
+
if !@result_rows.first then
|
117
|
+
for tuple_counter in 0...@result.num_tuples do
|
118
|
+
get_row(tuple_counter)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
@result_rows
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
db_name = 'artest'
|
127
|
+
connection = PGconn.connect(Lore.pg_server, Lore.pg_port, '', '', db_name.to_s, Lore.user_for(db_name), Lore.pass_for(db_name.to_s))
|
128
|
+
|
129
|
+
sql = 'SELECT *
|
130
|
+
FROM public.articles
|
131
|
+
JOIN public.contents on (public.contents.id = public.articles.content_id)
|
132
|
+
WHERE public.articles.id BETWEEN 1100 AND 1200 LIMIT 100 OFFSET 0'
|
133
|
+
|
134
|
+
bmbm(12) { |test|
|
135
|
+
test.report("rows_raw") {
|
136
|
+
num_loops.times {
|
137
|
+
result = connection.exec(sql)
|
138
|
+
for row in 0...result.num_tuples do
|
139
|
+
for field_index in 0...result.num_fields do
|
140
|
+
result.getvalue(row,field_index)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
}
|
144
|
+
}
|
145
|
+
test.report("rows_raw_parsed") {
|
146
|
+
num_loops.times {
|
147
|
+
result = connection.exec(sql)
|
148
|
+
p = Result_Parser.new(result)
|
149
|
+
# puts p.get_row_c(result, 0)
|
150
|
+
p.get_rows
|
151
|
+
}
|
152
|
+
}
|
153
|
+
test.report("rows_lore") {
|
154
|
+
num_loops.times {
|
155
|
+
result = connection.exec(sql)
|
156
|
+
lore_result = Lore::Result.new('',result)
|
157
|
+
lore_result.get_rows.each { |row|
|
158
|
+
# noop
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
test.report("ac_instances") {
|
163
|
+
num_loops.times {
|
164
|
+
result = connection.exec(sql)
|
165
|
+
lore_result = Lore::Result.new('',result)
|
166
|
+
model_instances = []
|
167
|
+
lore_result.get_rows.each { |row|
|
168
|
+
i = Lore::Article.new(Lore::Article,row)
|
169
|
+
model_instances.push(i)
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
test.report("full_auto") {
|
174
|
+
Lore.disable_cache
|
175
|
+
num_loops.times {
|
176
|
+
Lore::Article.find(100).with(Lore::Article.id.in(1100..1200)).entities
|
177
|
+
}
|
178
|
+
}
|
179
|
+
test.report("ar") {
|
180
|
+
num_loops.times {
|
181
|
+
AR::Article.find(:all, :conditions => "id > 1100 AND id < 1200").each { |a|
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
test.report('using cache') {
|
186
|
+
Lore.enable_cache
|
187
|
+
num_loops.times {
|
188
|
+
Lore::Article.find(100).with(Lore::Article.id.in(1100..1200)).entities
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
|
data/bits.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
module Lore
|
3
|
+
|
4
|
+
def self.resolve_passed_value(values, table_name, attrib_name)
|
5
|
+
return { :value => values[attrib_name].to_s, :field => attrib_name }
|
6
|
+
idx = attrib_name.to_s.length
|
7
|
+
value = nil
|
8
|
+
while value.nil? && idx > 1 do
|
9
|
+
name_part = attrib_name.to_s[0..idx]
|
10
|
+
value = values[name_part]
|
11
|
+
if value == '' then
|
12
|
+
name_part = table_name + '.' << name_part
|
13
|
+
value = values[name_part]
|
14
|
+
end
|
15
|
+
|
16
|
+
idx -= 1
|
17
|
+
end
|
18
|
+
{ :value => value, :field => name_part }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class Hash
|
24
|
+
|
25
|
+
def deep_merge!(second)
|
26
|
+
merger = proc { |key,v1,v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
27
|
+
self.merge!(second, &merger)
|
28
|
+
end
|
29
|
+
|
30
|
+
def nested_hash(array)
|
31
|
+
node = self
|
32
|
+
array.each do |i|
|
33
|
+
node[i]=Hash.new if node[i].nil?
|
34
|
+
node = node[i]
|
35
|
+
end
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def merge_nested_hash!(nested_hash)
|
40
|
+
deep_merge!(nested_hash)
|
41
|
+
end
|
42
|
+
|
43
|
+
def keys_flat(keys_result=[])
|
44
|
+
keys.each { |k|
|
45
|
+
keys_result << k
|
46
|
+
child = self[k]
|
47
|
+
child.keys_flat(keys_result) if (child && child.kind_of?(Hash))
|
48
|
+
}
|
49
|
+
return keys_result
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
require('logger')
|
3
|
+
require('pstore')
|
4
|
+
require('digest/md5')
|
5
|
+
|
6
|
+
module Lore
|
7
|
+
module Cache
|
8
|
+
|
9
|
+
# When implementing your own entity cache, derive from and implement
|
10
|
+
# Abstract_Entity_Cache.
|
11
|
+
# Expected class methods are:
|
12
|
+
# - flush
|
13
|
+
# - read(query_string)
|
14
|
+
# - create(model_klass, query_string, result)
|
15
|
+
# - include?(query_string)
|
16
|
+
# - delete(cache_index)
|
17
|
+
# cache_index being an MD5 sum of the query_string that
|
18
|
+
# generated the cache entry.
|
19
|
+
# In case you want cache indices other than MD5 sums,
|
20
|
+
# define class method
|
21
|
+
# index_for(query_string)
|
22
|
+
#
|
23
|
+
# Every model may use a different caching implementation.
|
24
|
+
# To enable a specific caching implenentation for a model, use:
|
25
|
+
#
|
26
|
+
# class Your_Model < Lore::Model
|
27
|
+
# ...
|
28
|
+
# use_cache_impl The_Cache_Implementation_Klass
|
29
|
+
# ...
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
class Abstract_Entity_Cache
|
33
|
+
|
34
|
+
# Delete all cache entries for this model
|
35
|
+
def self.flush
|
36
|
+
raise ::Exception.new('Not implemented')
|
37
|
+
end
|
38
|
+
|
39
|
+
# Read cached result for a specific query string.
|
40
|
+
# Returns array of model instances.
|
41
|
+
def self.read(accessor, query_string)
|
42
|
+
raise ::Exception.new('Not implemented')
|
43
|
+
end
|
44
|
+
|
45
|
+
# Create cache entry for a specific query_string on a model.
|
46
|
+
# Expects result from given query string as it has to be
|
47
|
+
# returned when calling Cache_Implementation.read(query_string)
|
48
|
+
def self.create(accessor, query_string, result)
|
49
|
+
raise ::Exception.new('Not implemented')
|
50
|
+
end
|
51
|
+
|
52
|
+
# Whether there is a cache entry for a query or not.
|
53
|
+
def self.include?(accessor, query_string)
|
54
|
+
raise ::Exception.new('Not implemented')
|
55
|
+
end
|
56
|
+
|
57
|
+
# Delete a specific cache entry, parameter index being
|
58
|
+
# primary key (e.g. a hash value) in cache generated from
|
59
|
+
# a query.
|
60
|
+
def self.delete(index)
|
61
|
+
raise ::Exception.new('Not implemented')
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
module Cache_Helpers
|
67
|
+
def index_for(query)
|
68
|
+
Digest::MD5.hexdigest(query)
|
69
|
+
end
|
70
|
+
|
71
|
+
def storefile_of(model_name, query)
|
72
|
+
'/tmp/lore_cache__' << model_name + '_' << Digest::MD5.hexdigest(query)
|
73
|
+
end
|
74
|
+
|
75
|
+
def create_store(storefile_name)
|
76
|
+
store = PStore.new(storefile_name) unless storefile_name.nil?
|
77
|
+
return store
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
data/cache/bits.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Lore
|
3
|
+
module Cache
|
4
|
+
|
5
|
+
@@ignore_params = [ 'cb__model', 'cb__controller', 'cb__mode' ]
|
6
|
+
|
7
|
+
def self.store_name(klass_name, controller, mode, keys)
|
8
|
+
key_string = ''
|
9
|
+
keys.each_pair { |name, value|
|
10
|
+
key_string += '@@@'+name+'==' << value.to_s unless @@ignore_params.include? name
|
11
|
+
}
|
12
|
+
controller = '*' if controller == :all
|
13
|
+
mode = '*' if mode == :all
|
14
|
+
|
15
|
+
store_name = '/tmp/cb__cache__' << klass_name.to_s << '@@@' << controller.to_s << '@@@' << mode.to_s
|
16
|
+
store_name += key_string unless key_string == ''
|
17
|
+
|
18
|
+
return store_name
|
19
|
+
end
|
20
|
+
|
21
|
+
end # module
|
22
|
+
end # module
|