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