og 0.24.0 → 0.25.0
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/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>
|