og 0.31.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/doc/{AUTHORS → CONTRIBUTORS} +26 -10
  2. data/doc/LICENSE +2 -3
  3. data/doc/RELEASES +56 -7
  4. data/doc/tutorial.txt +15 -15
  5. data/lib/glue/cacheable.rb +2 -5
  6. data/lib/glue/hierarchical.rb +1 -4
  7. data/lib/glue/optimistic_locking.rb +0 -2
  8. data/lib/glue/orderable.rb +79 -75
  9. data/lib/glue/revisable.rb +19 -24
  10. data/lib/glue/searchable.rb +0 -2
  11. data/lib/glue/taggable.rb +31 -29
  12. data/lib/glue/timestamped.rb +4 -2
  13. data/lib/og.rb +50 -29
  14. data/lib/og/adapter.rb +19 -0
  15. data/lib/og/adapter/mysql.rb +212 -0
  16. data/lib/og/adapter/mysql/override.rb +34 -0
  17. data/lib/og/adapter/mysql/script.rb +15 -0
  18. data/lib/og/adapter/mysql/utils.rb +40 -0
  19. data/lib/og/adapter/postgresql.rb +231 -0
  20. data/lib/og/adapter/postgresql/override.rb +117 -0
  21. data/lib/og/adapter/postgresql/script.rb +15 -0
  22. data/lib/og/adapter/postgresql/utils.rb +35 -0
  23. data/lib/og/adapter/sqlite.rb +132 -0
  24. data/lib/og/adapter/sqlite/override.rb +33 -0
  25. data/lib/og/adapter/sqlite/script.rb +15 -0
  26. data/lib/og/collection.rb +35 -7
  27. data/lib/og/{evolution.rb → dump.rb} +4 -5
  28. data/lib/og/entity.rb +102 -173
  29. data/lib/og/entity/clone.rb +119 -0
  30. data/lib/og/errors.rb +0 -2
  31. data/lib/og/manager.rb +85 -37
  32. data/lib/og/relation.rb +52 -34
  33. data/lib/og/relation/belongs_to.rb +0 -2
  34. data/lib/og/relation/has_many.rb +27 -4
  35. data/lib/og/relation/joins_many.rb +41 -14
  36. data/lib/og/relation/many_to_many.rb +10 -0
  37. data/lib/og/relation/refers_to.rb +22 -5
  38. data/lib/og/store.rb +80 -86
  39. data/lib/og/store/sql.rb +710 -713
  40. data/lib/og/store/sql/evolution.rb +119 -0
  41. data/lib/og/store/sql/join.rb +155 -0
  42. data/lib/og/store/sql/utils.rb +149 -0
  43. data/lib/og/test/assertions.rb +1 -3
  44. data/lib/og/test/testcase.rb +0 -2
  45. data/lib/og/types.rb +2 -5
  46. data/lib/og/validation.rb +6 -9
  47. data/test/{og/mixin → glue}/tc_hierarchical.rb +3 -13
  48. data/test/glue/tc_og_paginate.rb +47 -0
  49. data/test/{og/mixin → glue}/tc_optimistic_locking.rb +2 -12
  50. data/test/{og/mixin → glue}/tc_orderable.rb +15 -23
  51. data/test/glue/tc_orderable2.rb +47 -0
  52. data/test/glue/tc_revisable.rb +3 -3
  53. data/test/{og/mixin → glue}/tc_taggable.rb +20 -10
  54. data/test/{og/mixin → glue}/tc_timestamped.rb +2 -12
  55. data/test/glue/tc_webfile.rb +36 -0
  56. data/test/og/CONFIG.rb +8 -11
  57. data/test/og/multi_validations_model.rb +14 -0
  58. data/test/og/store/tc_filesys.rb +3 -1
  59. data/test/og/store/tc_kirby.rb +16 -13
  60. data/test/og/store/tc_sti.rb +11 -11
  61. data/test/og/store/tc_sti2.rb +79 -0
  62. data/test/og/tc_build.rb +41 -0
  63. data/test/og/tc_cacheable.rb +3 -2
  64. data/test/og/tc_has_many.rb +96 -0
  65. data/test/og/tc_inheritance.rb +6 -4
  66. data/test/og/tc_joins_many.rb +93 -0
  67. data/test/og/tc_multi_validations.rb +5 -7
  68. data/test/og/tc_multiple.rb +7 -6
  69. data/test/og/tc_override.rb +13 -7
  70. data/test/og/tc_primary_key.rb +30 -0
  71. data/test/og/tc_relation.rb +8 -14
  72. data/test/og/tc_reldelete.rb +163 -0
  73. data/test/og/tc_reverse.rb +17 -14
  74. data/test/og/tc_scoped.rb +3 -11
  75. data/test/og/tc_setup.rb +13 -11
  76. data/test/og/tc_store.rb +21 -28
  77. data/test/og/tc_validation2.rb +2 -2
  78. data/test/og/tc_validation_loop.rb +17 -15
  79. metadata +109 -103
  80. data/INSTALL +0 -91
  81. data/ProjectInfo +0 -51
  82. data/README +0 -177
  83. data/doc/config.txt +0 -28
  84. data/examples/README +0 -23
  85. data/examples/mysql_to_psql.rb +0 -71
  86. data/examples/run.rb +0 -271
  87. data/lib/glue/tree.rb +0 -218
  88. data/lib/og/store/alpha/filesys.rb +0 -110
  89. data/lib/og/store/alpha/memory.rb +0 -295
  90. data/lib/og/store/alpha/sqlserver.rb +0 -256
  91. data/lib/og/store/kirby.rb +0 -490
  92. data/lib/og/store/mysql.rb +0 -415
  93. data/lib/og/store/psql.rb +0 -875
  94. data/lib/og/store/sqlite.rb +0 -348
  95. data/lib/og/store/sqlite2.rb +0 -241
  96. data/setup.rb +0 -1585
  97. data/test/og/tc_sti_find.rb +0 -35
@@ -0,0 +1,119 @@
1
+ module Og
2
+
3
+ #--
4
+ # Implement Og's automatic schema evolution features.
5
+ # Add schema evolution related methods to SqlStore.
6
+ #++
7
+
8
+ module Evolution
9
+
10
+ #--
11
+ # Override if needed in the actual Adapter implementation.
12
+ #++
13
+
14
+ def add_sql_field(klass, a, anno)
15
+ Logger.info "Adding field '#{a}' to '#{klass.table}'"
16
+ query "ALTER TABLE #{klass.table} ADD COLUMN #{field_sql_for_attribute a, anno}"
17
+ end
18
+ alias add_sql_column add_sql_field
19
+
20
+ #--
21
+ # Override if needed in the actual Adapter implementation.
22
+ #++
23
+
24
+ def remove_sql_field(klass, a)
25
+ Logger.info "Removing field '#{a}' from '#{klass.table}'"
26
+ query "ALTER TABLE #{klass.table} DROP COLUMN #{a}"
27
+ end
28
+ alias add_sql_column add_sql_field
29
+
30
+ #--
31
+ # Override if needed in the actual Adapter implementation.
32
+ #++
33
+
34
+ def rename_sql_table(_old, _new)
35
+ Logger.info "Rename table '#{_old}' to '#{_new}'"
36
+ query "ALTER TABLE #{_old} RENAME #{_new}"
37
+ end
38
+
39
+ # Evolve the schema (table in sql stores) for the given
40
+ # class. Compares the fields in the database schema with
41
+ # the serializable attributes of the given class and tries
42
+ # to fix mismatches by adding are droping columns.
43
+ #
44
+ # === Evolution options
45
+ #
46
+ # * :evolve_schema => :add (only add, dont remove columns)
47
+ # * :evolve_schema => :full (add and delete columns)
48
+ # * :evolve_schema => :warn (only emit warnings, DEFAULT_
49
+ # * :evolve_schema => false (no evolution)
50
+ #
51
+ # === Example
52
+ #
53
+ # Og.setup(
54
+ # ..
55
+ # :evolve_schema => :full
56
+ # ..
57
+ # )
58
+
59
+ def evolve_schema(klass)
60
+ return unless @options[:evolve_schema]
61
+
62
+ sql_fields = create_field_map(klass).keys
63
+ attrs = klass.serializable_attributes
64
+
65
+ # Add new fields to the table.
66
+
67
+ for field in attrs
68
+ unless sql_fields.include? field
69
+ unless @options[:evolve_schema] == :warn
70
+ add_sql_field klass, field, klass.ann(field)
71
+ else
72
+ Logger.warn "Missing field '#{field}' on table '#{klass.table}'!"
73
+ end
74
+ end
75
+ end
76
+
77
+ # Remove obsolete fields from the table.
78
+
79
+ for field in sql_fields
80
+ unless attrs.include? field
81
+ if @options[:evolve_schema] == :full
82
+ remove_sql_field klass, field
83
+ else
84
+ Logger.warn "Obsolete field '#{field}' found on table '#{klass.table}'!"
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ # Renames the schema (table in sql stores) for the given
91
+ # class.
92
+ #
93
+ # === Input
94
+ #
95
+ # * new_schema = the new schema (Class or table name)
96
+ # * old_schema = the old schema (Class or table name)
97
+ #
98
+ # === Example
99
+ #
100
+ # store.rename_schema(TicketArticle, Ticket::Article)
101
+
102
+ def rename_schema(old_schema, new_schema)
103
+ if old_schema.is_a? Class
104
+ old_schema = table(old_schema)
105
+ end
106
+
107
+ if new_schema.is_a? Class
108
+ new_schema = table(new_schema)
109
+ end
110
+
111
+ rename_sql_table(old_schema, new_schema)
112
+ end
113
+
114
+ end
115
+
116
+ SqlStore.send :include, Evolution
117
+
118
+ end
119
+
@@ -0,0 +1,155 @@
1
+ module Og
2
+
3
+ # Add join related utility methods in SqlUtils.
4
+
5
+ module SqlUtils
6
+
7
+ def join_object_ordering(obj1, obj2)
8
+ if obj1.class.to_s <= obj2.class.to_s
9
+ return obj1, obj2
10
+ else
11
+ return obj2, obj1, true
12
+ end
13
+ end
14
+
15
+ def join_class_ordering(class1, class2)
16
+ if class1.to_s <= class2.to_s
17
+ return class1, class2
18
+ else
19
+ return class2, class1, true
20
+ end
21
+ end
22
+
23
+ def build_join_name(class1, class2, postfix = nil)
24
+ # Don't reorder arguments, as this is used in places that
25
+ # have already determined the order they want.
26
+ "#{Og.table_prefix}j_#{tableize(class1)}_#{tableize(class2)}#{postfix}"
27
+ end
28
+
29
+ def join_table(class1, class2, postfix = nil)
30
+ first, second = join_class_ordering(class1, class2)
31
+ build_join_name(first, second, postfix)
32
+ end
33
+
34
+ def join_table_index(key)
35
+ "#{key}_idx"
36
+ end
37
+
38
+ def join_table_key(klass)
39
+ klass = klass.schema_inheritance_root_class if klass.schema_inheritance_child?
40
+ "#{klass.to_s.demodulize.underscore.downcase}_oid"
41
+ end
42
+
43
+ def join_table_keys(class1, class2)
44
+ if class1 == class2
45
+ # Fix for the self-join case.
46
+ return join_table_key(class1), "#{join_table_key(class2)}2"
47
+ else
48
+ return join_table_key(class1), join_table_key(class2)
49
+ end
50
+ end
51
+
52
+ def ordered_join_table_keys(class1, class2)
53
+ first, second = join_class_ordering(class1, class2)
54
+ return join_table_keys(first, second)
55
+ end
56
+
57
+ def join_table_info(relation, postfix = nil)
58
+
59
+ # some fixes for schema inheritance.
60
+
61
+ owner_class, target_class = relation.owner_class, relation.target_class
62
+
63
+ raise "Undefined owner_class in #{target_class}" unless owner_class
64
+ raise "Undefined target_class in #{owner_class}" unless target_class
65
+
66
+ owner_class = owner_class.schema_inheritance_root_class if owner_class.schema_inheritance_child?
67
+ target_class = target_class.schema_inheritance_root_class if target_class.schema_inheritance_child?
68
+
69
+ owner_key, target_key = join_table_keys(owner_class, target_class)
70
+ first, second, changed = join_class_ordering(owner_class, target_class)
71
+
72
+ if changed
73
+ first_key, second_key = target_key, owner_key
74
+ else
75
+ first_key, second_key = owner_key, target_key
76
+ end
77
+
78
+ table = (relation.table ?
79
+ relation.table :
80
+ join_table(owner_class, target_class, postfix)
81
+ )
82
+
83
+ return {
84
+ :table => table,
85
+ :owner_key => owner_key,
86
+ :owner_table => table(owner_class),
87
+ :target_key => target_key,
88
+ :target_table => table(target_class),
89
+ :first_table => table(first),
90
+ :first_key => first_key,
91
+ :first_index => join_table_index(first_key),
92
+ :second_table => table(second),
93
+ :second_key => second_key,
94
+ :second_index => join_table_index(second_key)
95
+ }
96
+ end
97
+
98
+ # Subclasses can override this if they need a different
99
+ # syntax.
100
+
101
+ def create_join_table_sql(join_table_info, suffix = 'NOT NULL', key_type = 'integer')
102
+ join_table = join_table_info[:table]
103
+ first_index = join_table_info[:first_index]
104
+ first_key = join_table_info[:first_key]
105
+ second_key = join_table_info[:second_key]
106
+ second_index = join_table_info[:second_index]
107
+
108
+ sql = []
109
+
110
+ sql << %{
111
+ CREATE TABLE #{join_table} (
112
+ #{first_key} integer NOT NULL,
113
+ #{second_key} integer NOT NULL,
114
+ PRIMARY KEY(#{first_key}, #{second_key})
115
+ )
116
+ }
117
+
118
+ # gmosx: not that useful?
119
+ # sql << "CREATE INDEX #{first_index} ON #{join_table} (#{first_key})"
120
+ # sql << "CREATE INDEX #{second_index} ON #{join_table} (#{second_key})"
121
+
122
+ return sql
123
+ end
124
+
125
+ end
126
+
127
+ # Extends SqlStore by adding join related methods.
128
+
129
+ class SqlStore < Store
130
+
131
+ # Relate two objects through an intermediate join table.
132
+ # Typically used in joins_many and many_to_many relations.
133
+
134
+ def join(obj1, obj2, table, options = nil)
135
+ first, second = join_object_ordering(obj1, obj2)
136
+ first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
137
+ if options
138
+ exec "INSERT INTO #{table} (#{first_key},#{second_key}, #{options.keys.join(',')}) VALUES (#{first.pk},#{second.pk}, #{options.values.map { |v| quote(v) }.join(',')})"
139
+ else
140
+ exec "INSERT INTO #{table} (#{first_key},#{second_key}) VALUES (#{first.pk}, #{second.pk})"
141
+ end
142
+ end
143
+
144
+ # Unrelate two objects be removing their relation from the
145
+ # join table.
146
+
147
+ def unjoin(obj1, obj2, table)
148
+ first, second = join_object_ordering(obj1, obj2)
149
+ first_key, second_key = ordered_join_table_keys(obj1.class, obj2.class)
150
+ exec "DELETE FROM #{table} WHERE #{first_key}=#{first.pk} AND #{second_key}=#{second.pk}"
151
+ end
152
+
153
+ end
154
+
155
+ end
@@ -0,0 +1,149 @@
1
+ require 'time'
2
+
3
+ module Og
4
+
5
+ # A collection of useful SQL utilities.
6
+
7
+ module SqlUtils
8
+
9
+ # Escape an SQL string
10
+
11
+ def escape(str)
12
+ return nil unless str
13
+ return str.gsub(/'/, "''")
14
+ end
15
+
16
+ # Convert a ruby time to an sql timestamp.
17
+ #--
18
+ # TODO: Optimize this.
19
+ #++
20
+
21
+ def timestamp(time = Time.now)
22
+ return nil unless time
23
+ return time.strftime("%Y-%m-%d %H:%M:%S")
24
+ end
25
+
26
+ # Output YYY-mm-dd
27
+ #--
28
+ # TODO: Optimize this.
29
+ #++
30
+
31
+ def date(date)
32
+ return nil unless date
33
+ return "#{date.year}-#{date.month}-#{date.mday}"
34
+ end
35
+
36
+ #--
37
+ # TODO: implement me!
38
+ #++
39
+
40
+ def blob(val)
41
+ val
42
+ end
43
+
44
+ # Parse an integer.
45
+
46
+ def parse_int(int)
47
+ int = int.to_i if int
48
+ return int
49
+ end
50
+
51
+ # Parse a float.
52
+
53
+ def parse_float(fl)
54
+ fl = fl.to_f if fl
55
+ fl
56
+ end
57
+
58
+ # Parse sql datetime
59
+ #--
60
+ # TODO: Optimize this.
61
+ #++
62
+
63
+ def parse_timestamp(str)
64
+ return nil unless str
65
+ return Time.parse(str)
66
+ end
67
+
68
+ # Input YYYY-mm-dd
69
+ #--
70
+ # TODO: Optimize this.
71
+ #++
72
+
73
+ def parse_date(str)
74
+ return nil unless str
75
+ return Date.strptime(str)
76
+ end
77
+
78
+ # Parse a boolean
79
+ # true, 1, t => true
80
+ # other => false
81
+
82
+ def parse_boolean(str)
83
+ return true if (str=='true' || str=='t' || str=='1')
84
+ return false
85
+ end
86
+
87
+ #--
88
+ # TODO: implement me!!
89
+ #++
90
+
91
+ def parse_blob(val)
92
+ val
93
+ end
94
+
95
+ # Escape the various Ruby types.
96
+
97
+ def quote(vals)
98
+ vals = [vals] unless vals.is_a?(Array)
99
+ quoted = vals.inject('') do |s,val|
100
+ s += case val
101
+ when Fixnum, Integer, Float
102
+ val ? val.to_s : 'NULL'
103
+ when String
104
+ val ? "'#{escape(val)}'" : 'NULL'
105
+ when Time
106
+ val ? "'#{timestamp(val)}'" : 'NULL'
107
+ when Date
108
+ val ? "'#{date(val)}'" : 'NULL'
109
+ when TrueClass, FalseClass
110
+ val ? "'t'" : 'NULL'
111
+ else
112
+ # gmosx: keep the '' for nil symbols.
113
+ val ? escape(val.to_yaml) : ''
114
+ end + ','
115
+ end
116
+ quoted.chop!
117
+ vals.size > 1 ? "(#{quoted})" : quoted
118
+ end
119
+
120
+ # Escape the Array Ruby type.
121
+
122
+ def quote_array(val)
123
+ case val
124
+ when Array
125
+ val.collect{ |v| quotea(v) }.join(',')
126
+ else
127
+ quote(val)
128
+ end
129
+ end
130
+ alias_method :quotea, :quote_array
131
+
132
+ # Apply table name conventions to a class name.
133
+
134
+ def tableize(klass)
135
+ "#{klass.to_s.gsub(/::/, "_").downcase}"
136
+ end
137
+
138
+ # Return the table name for the given class.
139
+
140
+ def table(klass)
141
+ #klass.ann.self[:sql_table] || klass.ann.self[:table] || "#{Og.table_prefix}#{tableize(klass)}"
142
+ (klass.ann.self[:sql_table] rescue nil) ||
143
+ (klass.ann.self[:table] rescue nil) ||
144
+ "#{Og.table_prefix}#{tableize(klass)}"
145
+ end
146
+
147
+ end
148
+
149
+ end
@@ -141,7 +141,7 @@ module Test::Unit::Assertions
141
141
  assert_block(msg) { cookie.value == value }
142
142
  end
143
143
 
144
- # :section: Glue::Template related assertions.
144
+ # :section: Nitro::Template related assertions.
145
145
 
146
146
  # :section: Redirection assertions.
147
147
 
@@ -171,5 +171,3 @@ module Test::Unit::Assertions
171
171
  end
172
172
 
173
173
  end
174
-
175
- # * George Moschovitis <gm@navel.gr>
@@ -51,5 +51,3 @@ class TestCase
51
51
  end
52
52
 
53
53
  end
54
-
55
- # * George Moschovitis <gm@navel.gr>
@@ -2,11 +2,11 @@ module Og
2
2
 
3
3
  # Some useful type macros to help when defining properties.
4
4
  # You can easily code your own type macros. Just return the
5
- # array that should be passed to the property macro.
5
+ # array that should be passed to the attr_xxx macros.
6
6
  #
7
7
  # === Example
8
8
  #
9
- # property :name, VarChar(30)
9
+ # attr_accessor :name, VarChar(30)
10
10
 
11
11
  def self.VarChar(size)
12
12
  return String, :sql => "VARCHAR(#{size})"
@@ -17,6 +17,3 @@ NotNull = { :sql => 'NOT NULL' }.freeze
17
17
  Null = { :sql => 'NULL' }.freeze
18
18
 
19
19
  end
20
-
21
- # * Michael Neumann <mneumann@ntecs.de>
22
- # * George Moschovitis <gm@navel.gr>