og 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: meta.rb 326 2005-03-28 11:07:17Z gmosx $
3
+ # $Id: meta.rb 337 2005-03-31 16:20:40Z gmosx $
4
4
  #--
5
5
  # TODO:
6
6
  # - precreate the meta sql statements as much as possible to
@@ -11,8 +11,6 @@ require 'glue/inflector'
11
11
  require 'og/adapter'
12
12
  require 'og/typemacros'
13
13
 
14
- require 'og/mixins/list'
15
-
16
14
  module Og
17
15
 
18
16
  class Relation < N::Property
@@ -395,7 +393,6 @@ end
395
393
  if Og.include_meta_language
396
394
  class Module # :nodoc: all
397
395
  include Og::MetaLanguage
398
- include Og::List
399
396
  =begin
400
397
  A hack to avoid forward references. Does not work
401
398
  with namespave modules though. Any idea?
@@ -0,0 +1,134 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: hierarchical.rb 341 2005-04-04 08:28:54Z gmosx $
4
+
5
+ require 'glue/dynamic_include'
6
+
7
+ module Og
8
+
9
+ # Implements the Nested Sets pattern for hierarchical
10
+ # SQL queries.
11
+
12
+ module NestedSets
13
+
14
+ def self.append_dynamic_features(base, options)
15
+ c = {
16
+ :left => 'lft',
17
+ :right => 'rgt',
18
+ :type => Fixnum,
19
+ :scope => '"1 = 1"',
20
+ :parent => N::Inflector.name(base),
21
+ :children => N::Inflector.plural_name(base)
22
+ }
23
+ c.update(options) if options
24
+
25
+ parent = "#{c[:parent]}_oid"
26
+ left = c[:left]
27
+ right = c[:right]
28
+ children = c[:children]
29
+ child = N::Inflector.singularize(children)
30
+
31
+ if c[:scope].is_a?(Symbol) && c[:scope].to_s !~ /_oid$/
32
+ c[:scope] = "#{c[:scope]}_oid".intern
33
+ end
34
+ scope = c[:scope]
35
+ if scope.is_a?(Symbol)
36
+ scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
37
+ end
38
+
39
+ base.module_eval <<-EOE, __FILE__, __LINE__
40
+ property :#{parent}, Fixnum, :sql_index => true
41
+ property :#{left}, :#{right}, #{c[:type]}
42
+
43
+ def root?
44
+ (@#{parent}.nil? || @#{parent} == 0) && (@#{left} == 1) && (@#{right} > @#{left})
45
+ end
46
+
47
+ def child?
48
+ (@#{parent} && @#{parent} != 0) && (@#{left} > 1) && (@#{right} > @#{left})
49
+ end
50
+
51
+ def #{children}_count
52
+ return (@#{right} - @#{left} - 1)/2
53
+ end
54
+
55
+ def full_#{children}(extrasql = nil)
56
+ #{base}.all("WHERE " + #{scope} + " AND (#{left} BETWEEN \#\{@#{left}\} AND \#{@#{right}}) \#{extrasql}")
57
+ end
58
+
59
+ def #{children}(extrasql = nil)
60
+ #{base}.all("WHERE " + #{scope} + " AND (#{left} > \#\{@#{left}\}) AND (#{right} < \#{@#{right}}) \#{extrasql}")
61
+ end
62
+
63
+ def direct_#{children}(extrasql = nil)
64
+ #{base}.all("WHERE " + #{scope} + " AND #{parent} = \#{@oid} \#{extrasql}")
65
+ end
66
+
67
+ def add_#{child}(child)
68
+ self.reload if @oid
69
+ child.reload if child.oid
70
+
71
+ if @#{left}.nil? || @#{left} == 0 || @#{right}.nil? || @#{right} == 0
72
+ @#{left} = 1
73
+ @#{right} = 2
74
+ end
75
+
76
+ child.#{parent} = @oid
77
+ child.#{left} = pivot = @#{right}
78
+ child.#{right} = pivot + 1
79
+ @#{right} = pivot + 2
80
+
81
+ self.class.transaction do
82
+ self.class.update("#{left} = (#{left} + 2)", "WHERE " + #{scope} + " AND #{left} >= \#{pivot}")
83
+ self.class.update("#{right} = (#{right} + 2)", "WHERE " + #{scope} + " AND #{right} >= \#{pivot}")
84
+ end
85
+
86
+ self.save
87
+ child.save
88
+ end
89
+
90
+ def self.og_pre_delete(conn, obj)
91
+ return unless (obj.#{left} and obj.#{right})
92
+
93
+ span = obj.#{right} - obj.#{left} + 1
94
+
95
+ (klass = obj.class).transaction do
96
+ klass.delete(#{scope} + " AND #{left} > \#{obj.#{left}} AND (#{right} < \#{obj.#{right}})")
97
+ klass.update("#{left} = (#{left} - \#{span})", "WHERE " + #{scope} + " AND #{left} >= \#{obj.#{right}}")
98
+ klass.update("#{right} = (#{right} - \#{span})", "WHERE " + #{scope} + " AND #{right} >= \#{obj.#{right}}")
99
+ end
100
+ end
101
+ EOE
102
+ end
103
+
104
+ end
105
+
106
+ # Transform the base class to a hierarchical node.
107
+ # A selection of different implementation strategies
108
+ # are provided.
109
+ #
110
+ # === Example
111
+ #
112
+ # class Comment
113
+ # include Hierarchical, :method => :nested_sets
114
+ # end
115
+ #
116
+ # [+:method+]
117
+ # :simple
118
+ # :nested_sets
119
+ # :nested_intervals
120
+
121
+ module Hierarchical
122
+
123
+ def self.append_dynamic_features(base, options)
124
+ c = {
125
+ :method => :nested_sets,
126
+ }
127
+ c.update(options) if options
128
+
129
+ base.include(NestedSets)
130
+ end
131
+
132
+ end
133
+
134
+ end
@@ -1,24 +1,26 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: list.rb 326 2005-03-28 11:07:17Z gmosx $
3
+ # $Id: orderable.rb 340 2005-04-04 08:26:58Z gmosx $
4
+
5
+ require 'glue/dynamic_include'
4
6
 
5
7
  module Og
6
8
 
7
9
  # Attach list/ordering methods to the enchanted class.
8
- #--
9
- # TODO:
10
- # Convert to new Og filter system.
11
- # Implement as super-mixin.
12
- #++
13
10
 
14
- module List
11
+ module Orderable
15
12
 
16
- # The enchanted object acts as a list item.
13
+ def self.append_dynamic_features(base, options)
14
+ c = {
15
+ :position => 'position',
16
+ :type => Fixnum,
17
+ :scope => '"1 = 1"'
18
+ }
19
+ c.update(options) if options
17
20
 
18
- def acts_as_list(options = {})
19
- c = { :position => 'position', :type => Fixnum, :scope => '1 = 1' }
20
- c.update(options) if options.is_a?(Hash)
21
- c[:scope] = "#{c[:scope]}_oid".intern if c[:scope].is_a?(Symbol) && c[:scope].to_s !~ /_oid$/
21
+ if c[:scope].is_a?(Symbol) && c[:scope].to_s !~ /_oid$/
22
+ c[:scope] = "#{c[:scope]}_oid".intern
23
+ end
22
24
 
23
25
  position = c[:position]
24
26
  scope = c[:scope]
@@ -27,7 +29,7 @@ module List
27
29
  scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
28
30
  end
29
31
 
30
- module_eval <<-EOE, __FILE__, __LINE__
32
+ base.module_eval <<-EOE, __FILE__, __LINE__
31
33
  property :#{position}, #{c[:type]}
32
34
 
33
35
  def og_pre_insert(conn)
@@ -40,7 +42,7 @@ module List
40
42
 
41
43
  def move_higher
42
44
  if higher = higher_item
43
- #{self}.transaction do
45
+ #{base}.transaction do
44
46
  higher.increment_position
45
47
  decrement_position
46
48
  end
@@ -49,7 +51,7 @@ module List
49
51
 
50
52
  def move_lower
51
53
  if lower = lower_item
52
- #{self}.transaction do
54
+ #{base}.transaction do
53
55
  lower.decrement_position
54
56
  increment_position
55
57
  end
@@ -57,14 +59,14 @@ module List
57
59
  end
58
60
 
59
61
  def move_to_top
60
- #{self}.transaction do
62
+ #{base}.transaction do
61
63
  increment_position_of_higher_items
62
64
  set_top_position
63
65
  end
64
66
  end
65
67
 
66
68
  def move_to_bottom
67
- #{self}.transaction do
69
+ #{base}.transaction do
68
70
  decrement_position_of_lower_items
69
71
  set_bottom_position
70
72
  end
@@ -85,12 +87,12 @@ module List
85
87
  end
86
88
 
87
89
  def higher_item
88
- #{self}.one(#{scope} + " AND #{position}=\#\{@#{position} - 1\}")
90
+ #{base}.one(#{scope} + " AND #{position}=\#\{@#{position} - 1\}")
89
91
  end
90
92
  alias_method :previous_item, :higher_item
91
93
 
92
94
  def lower_item
93
- #{self}.one(#{scope} + " AND #{position}=\#\{@#{position} + 1\}")
95
+ #{base}.one(#{scope} + " AND #{position}=\#\{@#{position} + 1\}")
94
96
  end
95
97
  alias_method :next_item, :lower_item
96
98
 
@@ -99,7 +101,7 @@ module List
99
101
  alias_method :first_item, :top_item
100
102
 
101
103
  def bottom_item
102
- #{self}.one(#{scope} + " ORDER BY #{position} DESC")
104
+ #{base}.one(#{scope} + " ORDER BY #{position} DESC")
103
105
  end
104
106
  alias_method :last_item, :last_item
105
107
 
@@ -139,15 +141,15 @@ module List
139
141
  end
140
142
 
141
143
  def increment_position_of_higher_items
142
- #{self}.update("#{position}=(#{position} + 1)", "WHERE " + #{scope} + " AND #{position} < \#\{@#{position}\}")
144
+ #{base}.update("#{position}=(#{position} + 1)", "WHERE " + #{scope} + " AND #{position} < \#\{@#{position}\}")
143
145
  end
144
146
 
145
147
  def increment_position_of_all_items
146
- #{self}.update("#{position}=(#{position} + 1)", "WHERE " + #{scope})
148
+ #{base}.update("#{position}=(#{position} + 1)", "WHERE " + #{scope})
147
149
  end
148
150
 
149
151
  def decrement_position_of_lower_items
150
- #{self}.update("#{position}=(#{position} - 1)", "WHERE " + #{scope} + " AND #{position} > \#\{@#{position}\}")
152
+ #{base}.update("#{position}=(#{position} - 1)", "WHERE " + #{scope} + " AND #{position} > \#\{@#{position}\}")
151
153
  end
152
154
  EOE
153
155
  end
@@ -1,6 +1,7 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
+ # * Michael Neumann <mneumann@ntecs.de>
2
3
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: typemacros.rb 281 2005-03-10 12:24:14Z gmosx $
4
+ # $Id: typemacros.rb 340 2005-04-04 08:26:58Z gmosx $
4
5
 
5
6
  module Og
6
7
 
@@ -16,8 +17,8 @@ def VarChar(size)
16
17
  return String, :sql => "VARCHAR(#{size})"
17
18
  end
18
19
 
19
- NotNull = {:sql => "NOT NULL"}.freeze
20
+ NotNull = { :sql => 'NOT NULL' }.freeze
20
21
 
21
- Null = {:sql => "NULL"}.freeze
22
+ Null = { :sql => 'NULL' }.freeze
22
23
 
23
24
  end
@@ -0,0 +1,79 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib')
2
+
3
+ require 'test/unit'
4
+ require 'ostruct'
5
+
6
+ require 'og/mixins/hierarchical'
7
+
8
+ require 'og'
9
+
10
+ $og = Og::Database.new(
11
+ :adapter => 'psql',
12
+ :database => 'test',
13
+ :user => 'postgres',
14
+ :password => 'navelrulez',
15
+ :drop => true
16
+ )
17
+
18
+ class TestCaseOgHierarchical < Test::Unit::TestCase # :nodoc: all
19
+ include N
20
+
21
+ class Comment
22
+ property :body, String
23
+ property :create_time, Time
24
+
25
+ include Og::NestedSets
26
+
27
+ def initialize(body = nil)
28
+ @body = body
29
+ @create_time = Time.now
30
+ end
31
+
32
+ def to_s
33
+ sprintf("%3d %3d %s", @lft, @rgt, @body)
34
+ end
35
+ end
36
+
37
+ def test_all
38
+ $og.auto_manage_classes
39
+
40
+ root = Comment.create('root')
41
+ c1 = Comment.new('1')
42
+ root.add_comment(c1)
43
+ c2 = Comment.new('1.1')
44
+ c1.add_comment(c2)
45
+ c3 = Comment.new('1.2')
46
+ c1.add_comment(c3)
47
+ c4 = Comment.new('1.1.1')
48
+ c2.add_comment(c4)
49
+ c5 = Comment.new('1.2.1')
50
+ c3.add_comment(c5)
51
+ c6 = Comment.new('1.1.1.1')
52
+ c4.add_comment(c6)
53
+ c7 = Comment.new('2')
54
+ root.add_comment(c7)
55
+ c8 = Comment.new('3')
56
+ root.add_comment(c8)
57
+ c9 = Comment.new('2.1')
58
+ c7.add_comment(c9)
59
+
60
+ c1.reload
61
+ =begin
62
+ Comment.all("ORDER BY lft, rgt").each { |c|
63
+ puts sprintf("%3d %3d %s", c.lft, c.rgt, c.body)
64
+ # p c
65
+ }
66
+ puts '--1'
67
+ c1.comments("ORDER BY lft, rgt").each { |c| puts c.body }
68
+ puts '--2'
69
+ c1.full_comments("ORDER BY lft, rgt").each { |c| puts c.body }
70
+ puts '--3'
71
+ c1.direct_comments("ORDER BY lft, rgt").each { |c| puts c.body }
72
+ =end
73
+
74
+ assert_equal 6, c1.full_comments.size
75
+ assert_equal 5, c1.comments.size
76
+ assert_equal 2, c1.direct_comments.size
77
+ end
78
+
79
+ end
@@ -3,6 +3,8 @@ $:.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib')
3
3
  require 'test/unit'
4
4
  require 'ostruct'
5
5
 
6
+ require 'og/mixins/orderable'
7
+
6
8
  require 'og'
7
9
 
8
10
  $og = Og::Database.new(
@@ -13,7 +15,7 @@ $og = Og::Database.new(
13
15
  :drop => true
14
16
  )
15
17
 
16
- class TestCaseOgList < Test::Unit::TestCase # :nodoc: all
18
+ class TestCaseOgOrderable < Test::Unit::TestCase # :nodoc: all
17
19
  include N
18
20
 
19
21
  class Comment; end
@@ -30,8 +32,9 @@ class TestCaseOgList < Test::Unit::TestCase # :nodoc: all
30
32
  class Comment
31
33
  property :body, String
32
34
  belongs_to :article, Article
33
- acts_as_list :scope => :article
34
35
 
36
+ include Og::Orderable, :scope => :article
37
+
35
38
  def initialize(body = nil)
36
39
  @body = body
37
40
  end
@@ -0,0 +1,93 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib')
2
+
3
+ require 'test/unit'
4
+ require 'ostruct'
5
+
6
+ require 'og'
7
+ #require 'og/adapters/sqlserver'
8
+
9
+ class TC_OgSqlserver < Test::Unit::TestCase # :nodoc: all
10
+ include N
11
+
12
+ # Forward declaration.
13
+
14
+ class Comment; end
15
+
16
+ class Article
17
+ prop_accessor :name, String
18
+ prop_accessor :age, Fixnum
19
+ has_many :comments, Comment
20
+
21
+ def initialize (name = nil, age = nil)
22
+ @name, @age = name, age
23
+ end
24
+ end
25
+
26
+ class Comment
27
+ prop_accessor :text, String
28
+ belongs_to :article, Article
29
+
30
+ def initialize(text = nil)
31
+ @text = text
32
+ end
33
+ end
34
+
35
+ def setup
36
+ config = {
37
+ :adapter => 'sqlserver',
38
+ :address => 'localhost',
39
+ :database => 'test',
40
+ :user => 'sa',
41
+ :password => 'sa',
42
+ :connection_count => 2
43
+ }
44
+
45
+ $DBG = true
46
+
47
+ # Og::Database.drop_db!(config)
48
+ @og = Og::Database.new(config)
49
+ end
50
+
51
+ def teardown
52
+ @og.shutdown
53
+ end
54
+
55
+ def test_all
56
+
57
+ puts Article.all.size
58
+ Article.each {|a| a.delete! }
59
+ puts Article.all.size
60
+
61
+ a = Article.new('gmosx', 30)
62
+ a.save!
63
+
64
+ baseoid = a.oid
65
+
66
+ a1 = Article[baseoid]
67
+
68
+ assert_equal 'gmosx', a1.name
69
+ assert_equal 30, a1.age
70
+ assert_equal baseoid, a1.oid
71
+
72
+ Article.create('drak', 12)
73
+ Article.create('ekarak', 34)
74
+ Article.create('mario', 53)
75
+ Article.create('elathan', 34)
76
+
77
+ articles = Article.all
78
+
79
+ assert_equal 5, articles.size
80
+
81
+ a3 = Article['ekarak']
82
+
83
+ assert_equal 'ekarak', a3.name
84
+
85
+ c1 = Comment.new('a comment')
86
+ c1.save!
87
+ a3.add_comment(c1)
88
+
89
+ a5 = Article['ekarak']
90
+ assert_equal 1, a5.comments.size
91
+ end
92
+
93
+ end