og 0.24.0 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ProjectInfo +2 -5
- data/README +2 -0
- data/doc/AUTHORS +4 -1
- data/doc/RELEASES +53 -0
- data/examples/run.rb +2 -2
- data/lib/{og/mixin → glue}/hierarchical.rb +19 -19
- data/lib/{og/mixin → glue}/optimistic_locking.rb +1 -1
- data/lib/glue/orderable.rb +235 -0
- data/lib/glue/revisable.rb +2 -0
- data/lib/glue/taggable.rb +176 -0
- data/lib/{og/mixin/taggable.rb → glue/taggable_old.rb} +6 -0
- data/lib/glue/timestamped.rb +37 -0
- data/lib/{og/mixin → glue}/tree.rb +3 -8
- data/lib/og.rb +21 -20
- data/lib/og/collection.rb +15 -1
- data/lib/og/entity.rb +256 -114
- data/lib/og/manager.rb +60 -27
- data/lib/og/{mixin/schema_inheritance_base.rb → markers.rb} +5 -2
- data/lib/og/relation.rb +70 -74
- data/lib/og/relation/belongs_to.rb +5 -3
- data/lib/og/relation/has_many.rb +1 -0
- data/lib/og/relation/joins_many.rb +5 -4
- data/lib/og/store.rb +25 -46
- data/lib/og/store/alpha/filesys.rb +1 -1
- data/lib/og/store/alpha/kirby.rb +30 -30
- data/lib/og/store/alpha/memory.rb +49 -49
- data/lib/og/store/alpha/sqlserver.rb +7 -7
- data/lib/og/store/kirby.rb +38 -38
- data/lib/og/store/mysql.rb +43 -43
- data/lib/og/store/psql.rb +222 -53
- data/lib/og/store/sql.rb +165 -105
- data/lib/og/store/sqlite.rb +29 -25
- data/lib/og/validation.rb +24 -14
- data/lib/{vendor → og/vendor}/README +0 -0
- data/lib/{vendor → og/vendor}/kbserver.rb +1 -1
- data/lib/{vendor → og/vendor}/kirbybase.rb +230 -79
- data/lib/{vendor → og/vendor}/mysql.rb +0 -0
- data/lib/{vendor → og/vendor}/mysql411.rb +0 -0
- data/test/og/mixin/tc_hierarchical.rb +1 -1
- data/test/og/mixin/tc_optimistic_locking.rb +1 -1
- data/test/og/mixin/tc_orderable.rb +1 -1
- data/test/og/mixin/tc_taggable.rb +2 -2
- data/test/og/mixin/tc_timestamped.rb +2 -2
- data/test/og/tc_finder.rb +33 -0
- data/test/og/tc_inheritance.rb +2 -2
- data/test/og/tc_scoped.rb +45 -0
- data/test/og/tc_store.rb +1 -7
- metadata +21 -18
- data/lib/og/mixin/orderable.rb +0 -174
- data/lib/og/mixin/revisable.rb +0 -0
- data/lib/og/mixin/timestamped.rb +0 -24
data/ProjectInfo
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
TITLE : &title Og
|
4
4
|
NAME : &pkg og
|
5
|
-
VERSION : '0.
|
5
|
+
VERSION : '0.25.0'
|
6
6
|
STATUS : beta
|
7
7
|
|
8
8
|
AUTHOR : George Moschovitis
|
@@ -17,7 +17,7 @@ DESCRIPTION: >
|
|
17
17
|
KirbyBase, Filesystem and more.
|
18
18
|
|
19
19
|
DEPENDENCIES:
|
20
|
-
- [ glue, '= 0.
|
20
|
+
- [ glue, '= 0.25.0' ]
|
21
21
|
|
22
22
|
DISTRIBUTE: [ gem, tgz, zip ]
|
23
23
|
|
@@ -40,9 +40,6 @@ ANNOUNCE:
|
|
40
40
|
sectype: tls # ~, tls, ssl (tls is broke)
|
41
41
|
file: ANN
|
42
42
|
slogan: Og (ObjectGraph)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
43
|
links:
|
47
44
|
- http://www.nitrohq.com
|
48
45
|
|
data/README
CHANGED
@@ -38,6 +38,7 @@ The library provides the following features:
|
|
38
38
|
schema.
|
39
39
|
* ActiveRecord-style domain specific language and db synchronized
|
40
40
|
collections.
|
41
|
+
* Scoped queries on collections.
|
41
42
|
* Transforms resultsets from arbitrary sql queries to Ruby objects.
|
42
43
|
* Independent store for each object class, can support multiple
|
43
44
|
stores in the same application.
|
@@ -54,6 +55,7 @@ The library provides the following features:
|
|
54
55
|
* Hierarchical structures (nested sets)
|
55
56
|
* Works safely as part of distributed application.
|
56
57
|
* Optimistic locking.
|
58
|
+
* Dynamic finder methods.
|
57
59
|
* Simple implementation.
|
58
60
|
|
59
61
|
|
data/doc/AUTHORS
CHANGED
@@ -11,7 +11,10 @@ IDEAS, ADDITIONAL CODING, SUPPORT:
|
|
11
11
|
|
12
12
|
* Ysabel <deb@ysabel.org>
|
13
13
|
Refactoring, patches.
|
14
|
-
|
14
|
+
|
15
|
+
* Thomas Sawyer <transfire@gmail.com>
|
16
|
+
Refactoring, code cleanup.
|
17
|
+
|
15
18
|
* Guillaume Pierronnet <guillaume.pierronnet@laposte.net>
|
16
19
|
Tons of patches.
|
17
20
|
|
data/doc/RELEASES
CHANGED
@@ -1,3 +1,56 @@
|
|
1
|
+
== Version 0.25.0
|
2
|
+
|
3
|
+
This is the first in a series of releases focused on stability
|
4
|
+
and refinement. Many bugs where fixed, the high level api was
|
5
|
+
improved where needed, and we still got some small but incredibly
|
6
|
+
useful new features. Enjoy!
|
7
|
+
|
8
|
+
Most notable changes:
|
9
|
+
|
10
|
+
* Support for constrained / scoped queries in Og, here are
|
11
|
+
some examples:
|
12
|
+
|
13
|
+
User.with_scope(:condition => 'age > 2') {
|
14
|
+
users = User.all
|
15
|
+
}
|
16
|
+
|
17
|
+
Users.articles.find "title LIKE %t%" # => constrain i users articles.
|
18
|
+
|
19
|
+
* Dynamic auto generators, you can now query the database in
|
20
|
+
English:
|
21
|
+
|
22
|
+
User.find_by_name_and_age('gmosx', 'age')
|
23
|
+
User.find_or_create_by_name_and_age(...)
|
24
|
+
|
25
|
+
* Added experimental version of a new schema evolution system. Assuming
|
26
|
+
evolve_schema = true and evolve_schema_cautious = false
|
27
|
+
|
28
|
+
* With this patch, on application startup, fields are added and deleted.
|
29
|
+
* During run-time, if the file containing Og.setup is touched, fields are added.
|
30
|
+
* Fields are _not_ deleted during run-time, only at application startup.
|
31
|
+
|
32
|
+
a the moment this works only in the PostgreSQL store, support for more
|
33
|
+
stores is coming in the next versions. Thanks to Rob Pitt and Bryan Sotto
|
34
|
+
for this feature.
|
35
|
+
|
36
|
+
* Added some useful helpers to make the code you write cleaner,
|
37
|
+
here are some examples:
|
38
|
+
|
39
|
+
class Article
|
40
|
+
is Taggable
|
41
|
+
|
42
|
+
instead of
|
43
|
+
|
44
|
+
class Article
|
45
|
+
include Og::Taggable
|
46
|
+
|
47
|
+
and stuff like that...
|
48
|
+
|
49
|
+
* General code cleanup and refactoring.
|
50
|
+
|
51
|
+
* Many, many bug fixes, including security fixes.
|
52
|
+
|
53
|
+
|
1
54
|
== Version 0.24.0
|
2
55
|
|
3
56
|
A snapshot of the latest developments. This version features
|
data/examples/run.rb
CHANGED
@@ -50,7 +50,7 @@ class Article
|
|
50
50
|
# is used for serializing the attribute.
|
51
51
|
# no need to define the class, but you can if you want.
|
52
52
|
|
53
|
-
property :options
|
53
|
+
property :options, Hash
|
54
54
|
|
55
55
|
# exactly like the standard Ruby attr creates only the reader.
|
56
56
|
|
@@ -136,7 +136,7 @@ end
|
|
136
136
|
|
137
137
|
config = {
|
138
138
|
:destroy => true, # destroy table created from earlier runs.
|
139
|
-
:store =>
|
139
|
+
:store => :sqlite,
|
140
140
|
:name => 'test',
|
141
141
|
:user => "postgres",
|
142
142
|
:password => "navelrulez"
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'mega/dynamod'
|
2
2
|
require 'mega/orm_support'
|
3
3
|
|
4
|
-
module
|
4
|
+
module Glue
|
5
5
|
|
6
|
-
# Implements the Nested Sets pattern for hierarchical
|
6
|
+
# Implements the Nested Sets pattern for hierarchical
|
7
7
|
# SQL queries.
|
8
8
|
#--
|
9
9
|
# TODO: use active collections.
|
@@ -12,12 +12,12 @@ module Og
|
|
12
12
|
module NestedSets
|
13
13
|
|
14
14
|
def self.append_dynamic_features(base, options)
|
15
|
-
c = {
|
16
|
-
:left => 'lft',
|
17
|
-
:right => 'rgt',
|
18
|
-
:type => Fixnum,
|
15
|
+
c = {
|
16
|
+
:left => 'lft',
|
17
|
+
:right => 'rgt',
|
18
|
+
:type => Fixnum,
|
19
19
|
:parent => base.to_s.demodulize.underscore.downcase,
|
20
|
-
:children => base.to_s.demodulize.underscore.downcase.plural
|
20
|
+
:children => base.to_s.demodulize.underscore.downcase.plural
|
21
21
|
}
|
22
22
|
c.update(options) if options
|
23
23
|
|
@@ -32,29 +32,29 @@ module NestedSets
|
|
32
32
|
end
|
33
33
|
|
34
34
|
scope = c[:scope]
|
35
|
-
|
35
|
+
|
36
36
|
if scope
|
37
37
|
if scope.is_a?(Symbol)
|
38
38
|
scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
cond = 'condition => ' + scope
|
42
42
|
cond_and = ':condition => ' + scope + ' + " AND " +'
|
43
43
|
else
|
44
44
|
cond = ':condition => nil'
|
45
45
|
cond_and = ':condition => '
|
46
|
-
end
|
46
|
+
end
|
47
47
|
|
48
48
|
base.module_eval <<-EOE, __FILE__, __LINE__
|
49
49
|
property :#{parent}, Fixnum, :sql_index => true
|
50
50
|
property :#{left}, :#{right}, #{c[:type]}
|
51
51
|
|
52
52
|
def root?
|
53
|
-
(@#{parent}.nil? || @#{parent} == 0) && (@#{left} == 1) && (@#{right} > @#{left})
|
53
|
+
(@#{parent}.nil? || @#{parent} == 0) && (@#{left} == 1) && (@#{right} > @#{left})
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def child?
|
57
|
-
(@#{parent} && @#{parent} != 0) && (@#{left} > 1) && (@#{right} > @#{left})
|
57
|
+
(@#{parent} && @#{parent} != 0) && (@#{left} > 1) && (@#{right} > @#{left})
|
58
58
|
end
|
59
59
|
|
60
60
|
def parent
|
@@ -64,7 +64,7 @@ module NestedSets
|
|
64
64
|
#{base}[@#{parent}]
|
65
65
|
end
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
def #{children}_count
|
69
69
|
return (@#{right} - @#{left} - 1)/2
|
70
70
|
end
|
@@ -78,7 +78,7 @@ module NestedSets
|
|
78
78
|
options.update(#{cond_and}"(#{left} > \#\{@#{left}\}) AND (#{right} < \#{@#{right}})")
|
79
79
|
#{base}.all(options)
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
def direct_#{children}(options = {})
|
83
83
|
options.update(#{cond_and}"#{parent} = \#{pk}")
|
84
84
|
#{base}.all(options)
|
@@ -87,12 +87,12 @@ module NestedSets
|
|
87
87
|
def add_#{child}(child)
|
88
88
|
self.reload if pk
|
89
89
|
child.reload if child.pk
|
90
|
-
|
90
|
+
|
91
91
|
if @#{left}.nil? || @#{left} == 0 || @#{right}.nil? || @#{right} == 0
|
92
92
|
@#{left} = 1
|
93
93
|
@#{right} = 2
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
child.#{parent} = pk
|
97
97
|
child.#{left} = pivot = @#{right}
|
98
98
|
child.#{right} = pivot + 1
|
@@ -102,7 +102,7 @@ module NestedSets
|
|
102
102
|
#{base}.update_property("#{left} = (#{left} + 2)", #{cond_and}"#{left} >= \#{pivot}")
|
103
103
|
#{base}.update_property("#{right} = (#{right} + 2)", #{cond_and}"#{right} >= \#{pivot}")
|
104
104
|
end
|
105
|
-
|
105
|
+
|
106
106
|
self.save
|
107
107
|
child.save
|
108
108
|
end
|
@@ -129,7 +129,7 @@ end
|
|
129
129
|
module Hierarchical
|
130
130
|
|
131
131
|
def self.append_dynamic_features(base, options)
|
132
|
-
o = {
|
132
|
+
o = {
|
133
133
|
:method => :nested_sets,
|
134
134
|
}
|
135
135
|
o.update(options) if options
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'mega/dynamod'
|
2
|
+
require 'glue/aspects'
|
3
|
+
|
4
|
+
module Glue
|
5
|
+
|
6
|
+
# Attach list/ordering methods to the enchanted class.
|
7
|
+
|
8
|
+
module Orderable
|
9
|
+
include Glue::Aspects
|
10
|
+
|
11
|
+
dynamic_feature do |opt|
|
12
|
+
|
13
|
+
opt_position = opt[:position] || 'position'
|
14
|
+
|
15
|
+
opt_type = opt[:type] || Fixnum
|
16
|
+
|
17
|
+
# provide a user defined condition.
|
18
|
+
|
19
|
+
opt_condition = opt[:condition]
|
20
|
+
|
21
|
+
# provide a condition based on a key field (?)
|
22
|
+
|
23
|
+
opt_scope = opt[:scope]
|
24
|
+
|
25
|
+
# clean scope field.
|
26
|
+
|
27
|
+
if opt_scope
|
28
|
+
if opt_scope.to_s !~ /_oid$/
|
29
|
+
opt_scope = "#{opt_scope}_oid".to_sym
|
30
|
+
else
|
31
|
+
opt_scope = opt_scope.to_sym
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
define_method :orderable_position do
|
36
|
+
opt_position
|
37
|
+
end
|
38
|
+
|
39
|
+
define_method :orderable_type do
|
40
|
+
opt_type
|
41
|
+
end
|
42
|
+
|
43
|
+
define_method :orderable_scope do
|
44
|
+
opt_scope
|
45
|
+
end
|
46
|
+
|
47
|
+
define_method :orderable_condition do
|
48
|
+
scope = orderable_scope
|
49
|
+
if scope
|
50
|
+
scope_value = send(scope)
|
51
|
+
scope = scope_value ? "#{scope} = #{scope_value}" : "#{scope} IS NULL"
|
52
|
+
end
|
53
|
+
return [ opt_condition, scope ].compact
|
54
|
+
end
|
55
|
+
|
56
|
+
# How to check if property is already defined?
|
57
|
+
|
58
|
+
unless method_defined?( opt_position )
|
59
|
+
define_method( opt_position ) do @position ; end
|
60
|
+
define_method( "#{opt_position}=" ) do |x| @position = x ; end
|
61
|
+
property opt_position, opt_type
|
62
|
+
end
|
63
|
+
|
64
|
+
# Use position for general reference.
|
65
|
+
|
66
|
+
attr_accessor :position
|
67
|
+
|
68
|
+
end #dynamic_feature
|
69
|
+
|
70
|
+
before "add_to_bottom", :on => :og_insert
|
71
|
+
before "decrement_position_of_lower_items", :on => :og_delete
|
72
|
+
|
73
|
+
# Move higher.
|
74
|
+
|
75
|
+
def move_higher
|
76
|
+
if higher = higher_item
|
77
|
+
self.class.transaction do
|
78
|
+
higher.increment_position
|
79
|
+
decrement_position
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Move lower.
|
85
|
+
|
86
|
+
def move_lower
|
87
|
+
if lower = lower_item
|
88
|
+
self.class.transaction do
|
89
|
+
lower.decrement_position
|
90
|
+
increment_position
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Move to the top.
|
96
|
+
|
97
|
+
def move_to_top
|
98
|
+
self.class.transaction do
|
99
|
+
increment_position_of_higher_items
|
100
|
+
set_top_position
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Move to the bottom.
|
105
|
+
|
106
|
+
def move_to_bottom
|
107
|
+
self.class.transaction do
|
108
|
+
decrement_position_of_lower_items
|
109
|
+
set_bottom_position
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Move to a specific position.
|
114
|
+
|
115
|
+
def move_to(dest_position)
|
116
|
+
return if @position == dest_position
|
117
|
+
|
118
|
+
pos = orderable_position
|
119
|
+
con = orderable_condition
|
120
|
+
|
121
|
+
self.class.transaction do
|
122
|
+
if @position < dest_position
|
123
|
+
adj = "#{pos} = #{pos} - 1"
|
124
|
+
con = con + [ "#{pos} > #{@position}", "#{pos} <= #{dest_position}" ]
|
125
|
+
else
|
126
|
+
adj = "#{pos} = #{pos} + 1"
|
127
|
+
con = con + [ "#{pos} < #{@position}", "#{pos} >= #{dest_position}" ]
|
128
|
+
end
|
129
|
+
self.class.update( adj, :condition => con.join(' AND ') )
|
130
|
+
@position = dest_position
|
131
|
+
update_property(:"#{pos}")
|
132
|
+
end
|
133
|
+
|
134
|
+
self
|
135
|
+
end
|
136
|
+
|
137
|
+
def add_to_top
|
138
|
+
increment_position_of_all_items
|
139
|
+
end
|
140
|
+
|
141
|
+
def add_to_bottom
|
142
|
+
@position = bottom_position + 1
|
143
|
+
end
|
144
|
+
|
145
|
+
def add_to
|
146
|
+
# TODO
|
147
|
+
end
|
148
|
+
|
149
|
+
def higher_item
|
150
|
+
pos = orderable_position
|
151
|
+
con = orderable_condition + [ "#{pos} = #{@position - 1}" ]
|
152
|
+
self.class.one( :condition => con.join(' AND ') )
|
153
|
+
end
|
154
|
+
alias_method :previous_item, :higher_item
|
155
|
+
|
156
|
+
def lower_item
|
157
|
+
pos = orderable_position
|
158
|
+
con = orderable_condition + [ "#{pos} = #{@position + 1}" ]
|
159
|
+
self.class.one( :condition => con.join(' AND ') )
|
160
|
+
end
|
161
|
+
alias_method :next_item, :lower_item
|
162
|
+
|
163
|
+
def top_item
|
164
|
+
# TODO
|
165
|
+
end
|
166
|
+
alias_method :first_item, :top_item
|
167
|
+
|
168
|
+
def bottom_item
|
169
|
+
pos = orderable_position
|
170
|
+
con = orderable_condition
|
171
|
+
con = con.empty? ? nil : con.join(' AND ')
|
172
|
+
self.class.one(:condition => con, :order => "#{pos} DESC", :limit => 1)
|
173
|
+
end
|
174
|
+
alias_method :last_item, :last_item
|
175
|
+
|
176
|
+
def top?
|
177
|
+
@position == 1
|
178
|
+
end
|
179
|
+
alias_method :first?, :top?
|
180
|
+
|
181
|
+
def bottom?
|
182
|
+
@position == bottom_position
|
183
|
+
end
|
184
|
+
alias_method :last?, :bottom?
|
185
|
+
|
186
|
+
def increment_position
|
187
|
+
@position += 1
|
188
|
+
update_property(:"#{orderable_position}")
|
189
|
+
end
|
190
|
+
|
191
|
+
def decrement_position
|
192
|
+
@position -= 1
|
193
|
+
update_property(:"#{orderable_position}")
|
194
|
+
end
|
195
|
+
|
196
|
+
def bottom_position
|
197
|
+
item = bottom_item
|
198
|
+
item ? item.send(orderable_position) : 0
|
199
|
+
end
|
200
|
+
|
201
|
+
def set_top_position
|
202
|
+
@position = 1
|
203
|
+
update_property(:"#{orderable_position}")
|
204
|
+
end
|
205
|
+
|
206
|
+
def set_bottom_position
|
207
|
+
@position = bottom_position + 1
|
208
|
+
update_property(:"#{orderable_position}")
|
209
|
+
end
|
210
|
+
|
211
|
+
def increment_position_of_higher_items
|
212
|
+
pos = orderable_position
|
213
|
+
con = orderable_condition + [ "#{pos} < #{@position}" ]
|
214
|
+
self.class.update( "#{pos}=(#{pos} + 1)", :condition => con.join(' AND ') )
|
215
|
+
end
|
216
|
+
|
217
|
+
def increment_position_of_all_items
|
218
|
+
pos = orderable_position
|
219
|
+
con = orderable_condition
|
220
|
+
con = con.empty? ? nil : con.join(' AND ')
|
221
|
+
self.class.update("#{pos}=(#{pos} + 1)", :condition => con )
|
222
|
+
end
|
223
|
+
|
224
|
+
def decrement_position_of_lower_items
|
225
|
+
pos = orderable_position
|
226
|
+
con = orderable_condition + [ "#{pos} > #{@position}" ]
|
227
|
+
self.class.update( "#{pos}=(#{pos} - 1)", :condition => con.join(' AND ') )
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
# * George Moschovitis <gm@navel.gr>
|
235
|
+
# * Thomas Sawyer <transfire@gmail.com>
|