og 0.31.0 → 0.40.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 (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>