og 0.24.0 → 0.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/ProjectInfo +2 -5
  2. data/README +2 -0
  3. data/doc/AUTHORS +4 -1
  4. data/doc/RELEASES +53 -0
  5. data/examples/run.rb +2 -2
  6. data/lib/{og/mixin → glue}/hierarchical.rb +19 -19
  7. data/lib/{og/mixin → glue}/optimistic_locking.rb +1 -1
  8. data/lib/glue/orderable.rb +235 -0
  9. data/lib/glue/revisable.rb +2 -0
  10. data/lib/glue/taggable.rb +176 -0
  11. data/lib/{og/mixin/taggable.rb → glue/taggable_old.rb} +6 -0
  12. data/lib/glue/timestamped.rb +37 -0
  13. data/lib/{og/mixin → glue}/tree.rb +3 -8
  14. data/lib/og.rb +21 -20
  15. data/lib/og/collection.rb +15 -1
  16. data/lib/og/entity.rb +256 -114
  17. data/lib/og/manager.rb +60 -27
  18. data/lib/og/{mixin/schema_inheritance_base.rb → markers.rb} +5 -2
  19. data/lib/og/relation.rb +70 -74
  20. data/lib/og/relation/belongs_to.rb +5 -3
  21. data/lib/og/relation/has_many.rb +1 -0
  22. data/lib/og/relation/joins_many.rb +5 -4
  23. data/lib/og/store.rb +25 -46
  24. data/lib/og/store/alpha/filesys.rb +1 -1
  25. data/lib/og/store/alpha/kirby.rb +30 -30
  26. data/lib/og/store/alpha/memory.rb +49 -49
  27. data/lib/og/store/alpha/sqlserver.rb +7 -7
  28. data/lib/og/store/kirby.rb +38 -38
  29. data/lib/og/store/mysql.rb +43 -43
  30. data/lib/og/store/psql.rb +222 -53
  31. data/lib/og/store/sql.rb +165 -105
  32. data/lib/og/store/sqlite.rb +29 -25
  33. data/lib/og/validation.rb +24 -14
  34. data/lib/{vendor → og/vendor}/README +0 -0
  35. data/lib/{vendor → og/vendor}/kbserver.rb +1 -1
  36. data/lib/{vendor → og/vendor}/kirbybase.rb +230 -79
  37. data/lib/{vendor → og/vendor}/mysql.rb +0 -0
  38. data/lib/{vendor → og/vendor}/mysql411.rb +0 -0
  39. data/test/og/mixin/tc_hierarchical.rb +1 -1
  40. data/test/og/mixin/tc_optimistic_locking.rb +1 -1
  41. data/test/og/mixin/tc_orderable.rb +1 -1
  42. data/test/og/mixin/tc_taggable.rb +2 -2
  43. data/test/og/mixin/tc_timestamped.rb +2 -2
  44. data/test/og/tc_finder.rb +33 -0
  45. data/test/og/tc_inheritance.rb +2 -2
  46. data/test/og/tc_scoped.rb +45 -0
  47. data/test/og/tc_store.rb +1 -7
  48. metadata +21 -18
  49. data/lib/og/mixin/orderable.rb +0 -174
  50. data/lib/og/mixin/revisable.rb +0 -0
  51. data/lib/og/mixin/timestamped.rb +0 -24
@@ -2,7 +2,7 @@
2
2
 
3
3
  TITLE : &title Og
4
4
  NAME : &pkg og
5
- VERSION : '0.24.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.24.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
 
@@ -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
 
@@ -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
@@ -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 => 'psql',
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 Og
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
@@ -1,7 +1,7 @@
1
1
  require 'mega/dynamod'
2
2
  require 'glue/on_included'
3
3
 
4
- module Og
4
+ module Glue
5
5
 
6
6
  # This error is thrown when you the object you are trynig
7
7
  # to update is allready updated by another thread.
@@ -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>