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,34 @@
1
+ #--
2
+ # Customize the standard MySQL driver objects to make
3
+ # more compatible with Og.
4
+ #++
5
+
6
+ class Mysql # :nodoc: all
7
+
8
+ class Result # :nodoc: all
9
+ def blank?
10
+ 0 == num_rows
11
+ end
12
+
13
+ alias_method :next, :fetch_row
14
+
15
+ def each_row
16
+ each do |row|
17
+ yield(row, 0)
18
+ end
19
+ end
20
+
21
+ def first_value
22
+ val = fetch_row[0]
23
+ free
24
+ return val
25
+ end
26
+
27
+ alias close free
28
+
29
+ def fields
30
+ fetch_fields.collect { |f| f.name }
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,15 @@
1
+ # Helper for mysql scripts.
2
+ #
3
+ # === Example
4
+ #
5
+ # mysql '-u root -p', %{
6
+ # drop database if exists weblog_development;
7
+ # create database weblog_development;
8
+ # grant all on weblog_development.* to #{`id -un`.strip}@localhost;
9
+ # }
10
+
11
+ def mysql(opts, stream)
12
+ IO.popen("mysql #{opts}", 'w') do |io|
13
+ io.puts stream
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ require 'og/store/sql/utils'
2
+
3
+ module Og
4
+
5
+ module MysqlUtils
6
+ include SqlUtils
7
+
8
+ def escape(str)
9
+ return nil unless str
10
+ return Mysql.quote(str)
11
+ end
12
+
13
+ # Escape the various Ruby types.
14
+
15
+ def quote(vals)
16
+ vals = [vals] unless vals.is_a?(Array)
17
+ quoted = vals.inject('') do |s, val|
18
+ s += case val
19
+ when Fixnum, Integer, Float
20
+ val ? val.to_s : 'NULL'
21
+ when String
22
+ val ? "'#{escape(val)}'" : 'NULL'
23
+ when Time
24
+ val ? "'#{timestamp(val)}'" : 'NULL'
25
+ when Date
26
+ val ? "'#{date(val)}'" : 'NULL'
27
+ when TrueClass, FalseClass
28
+ val ? "'1'" : 'NULL'
29
+ else
30
+ # gmosx: keep the '' for nil symbols.
31
+ val ? escape(val.to_yaml) : ''
32
+ end + ','
33
+ end
34
+ quoted.chop!
35
+ vals.size > 1 ? "(#{quoted})" : quoted
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,231 @@
1
+ begin
2
+ require 'postgres'
3
+ rescue Object => ex
4
+ Logger.error 'Ruby-PostgreSQL bindings are not installed!'
5
+ Logger.error ex
6
+ end
7
+
8
+ require 'og/store/sql'
9
+ require 'og/adapter/postgresql/override'
10
+ require 'og/adapter/postgresql/utils'
11
+
12
+ module Og
13
+
14
+ # A Store that persists objects into a PostgreSQL database.
15
+ # To read documentation about the methods, consult the
16
+ # documentation for SqlStore and Store.
17
+ #
18
+ # This is the reference Og store.
19
+
20
+ class PostgresqlAdapter < SqlStore
21
+ extend PostgresqlUtils; include PostgresqlUtils
22
+
23
+ def initialize(options)
24
+ super
25
+
26
+ @typemap.update(Og::Blob => 'bytea')
27
+
28
+ @conn = PGconn.connect(
29
+ options[:address] || options[:host],
30
+ options[:port], nil, nil,
31
+ options[:name],
32
+ options[:user].to_s,
33
+ options[:password].to_s
34
+ )
35
+ schema_order = options[:schema_order]
36
+ encoding = options[:encoding]
37
+ min_messages = options[:min_messages]
38
+
39
+ @conn.exec("SET search_path TO #{schema_order}") if schema_order
40
+ @conn.exec("SET client_encoding TO '#{encoding}'") if encoding
41
+ @conn.exec("SET client_min_messages TO '#{min_messages}'") if min_messages
42
+ rescue PGError => ex
43
+ if database_does_not_exist_exception? ex
44
+ Logger.info "Database '#{options[:name]}' not found!"
45
+ create_db(options)
46
+ retry
47
+ end
48
+ raise
49
+ end
50
+
51
+ def close
52
+ @conn.close
53
+ super
54
+ end
55
+
56
+ def create_db(options)
57
+ # gmosx: system is used to avoid shell expansion.
58
+ system 'createdb', options[:name], '-U', options[:user]
59
+ super
60
+ end
61
+
62
+ def destroy_db(options)
63
+ system 'dropdb', options[:name], '-U', options[:user]
64
+ super
65
+ end
66
+
67
+ # The type used for default primary keys.
68
+
69
+ def primary_key_type
70
+ 'serial PRIMARY KEY'
71
+ end
72
+
73
+ def enchant(klass, manager)
74
+ pk = klass.primary_key
75
+
76
+ seq = if klass.schema_inheritance_child?
77
+ "#{table(klass.schema_inheritance_root_class)}_#{pk}_seq"
78
+ else
79
+ "#{table(klass)}_#{pk}_seq"
80
+ end
81
+
82
+ pkann = klass.ann[pk]
83
+
84
+ pkann[:sequence] = (pkann[:sql] =~ /SERIAL/i) ? seq : false
85
+
86
+ super
87
+ end
88
+
89
+ def query_statement(sql)
90
+ return @conn.exec(sql)
91
+ end
92
+
93
+ def exec_statement(sql)
94
+ @conn.exec(sql).clear
95
+ end
96
+
97
+ def sql_update(sql)
98
+ Logger.debug sql if $DBG
99
+ res = @conn.exec(sql)
100
+ changed = res.cmdtuples
101
+ res.clear
102
+ return changed
103
+ end
104
+
105
+ # Return the last inserted row id.
106
+
107
+ def last_insert_id(klass)
108
+ seq = klass.ann[klass.primary_key][:sequence]
109
+
110
+ res = query("SELECT nextval('#{seq}')")
111
+ lid = Integer(res.first_value)
112
+
113
+ return lid
114
+ end
115
+
116
+ # The insert sql statements.
117
+
118
+ def insert_sql(sql, klass)
119
+ str = ''
120
+
121
+ if klass.ann[klass.primary_key][:sequence]
122
+ str << "@#{klass.primary_key} = store.last_insert_id(#{klass})\n"
123
+ end
124
+
125
+ str << "store.exec \"#{sql}\""
126
+
127
+ return str
128
+ end
129
+
130
+ # :section: Transaction methods.
131
+
132
+ # Start a new transaction.
133
+
134
+ def start
135
+ # neumann: works with earlier PSQL databases too.
136
+ exec('BEGIN TRANSACTION') if @transaction_nesting < 1
137
+
138
+ if @transaction_nesting >= 1 && @conn.server_version > 80000
139
+ exec("SAVEPOINT SP#{@transaction_nesting}")
140
+ end
141
+
142
+ @transaction_nesting += 1
143
+ end
144
+
145
+ # Commit a transaction.
146
+
147
+ def commit
148
+ @transaction_nesting -= 1
149
+ exec('COMMIT') if @transaction_nesting < 1
150
+
151
+ if @transaction_nesting >= 1 && @conn.server_version > 80000
152
+ exec("RELEASE SAVEPOINT SP#{@transaction_nesting}")
153
+ end
154
+ end
155
+
156
+ # Rollback a transaction.
157
+
158
+ def rollback
159
+ @transaction_nesting -= 1
160
+ exec('ROLLBACK') if @transaction_nesting < 1
161
+
162
+ if @transaction_nesting >= 1 && @conn.server_version > 80000
163
+ exec("ROLLBACK TO SAVEPOINT SP#{@transaction_nesting}")
164
+ end
165
+ end
166
+
167
+ def read_attr(s, a, col)
168
+ store = self.class
169
+ {
170
+ String => nil,
171
+ Integer => :parse_int,
172
+ Float => :parse_float,
173
+ Time => :parse_timestamp,
174
+ Date => :parse_date,
175
+ TrueClass => :parse_boolean,
176
+ Og::Blob => :parse_blob
177
+ }.each do |klass, meth|
178
+ if a.class.ancestor? klass
179
+ return meth ?
180
+ "#{store}.#{meth}(res[#{col} + offset])" : "res[#{col} + offset]"
181
+ end
182
+ end
183
+
184
+ # else try to load it via YAML
185
+ "YAML::load(res[#{col} + offset])"
186
+ end
187
+
188
+ def eval_og_allocate(klass)
189
+ if klass.schema_inheritance?
190
+ klass.module_eval %{
191
+ def self.og_allocate(res, row = 0)
192
+ Object.constant(res[0]).allocate
193
+ end
194
+ }
195
+ else
196
+ klass.module_eval %{
197
+ def self.og_allocate(res, row = 0)
198
+ self.allocate
199
+ end
200
+ }
201
+ end
202
+ end
203
+
204
+ def read_row(obj, res, res_row, row)
205
+ res.fields.each_with_index do |field, idx|
206
+ obj.instance_variable_set "@#{field}", res.getvalue(row, idx)
207
+ end
208
+ end
209
+
210
+ # Returns the PostgreSQL information of a table within the database or
211
+ # nil if it doesn't exist. Mostly for internal usage.
212
+
213
+ def table_info(table)
214
+ r = query_statement("SELECT c.* FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND pg_catalog.pg_table_is_visible(c.oid) AND c.relname = '#{self.class.escape(table.to_s)}'")
215
+ return r && r.blank? ? nil : r.next
216
+ end
217
+
218
+ private
219
+
220
+ def database_does_not_exist_exception?(ex)
221
+ ex.message =~ /database .* does not exist/i
222
+ end
223
+
224
+ def table_already_exists_exception?(ex)
225
+ ex.message =~ /already exists/
226
+ end
227
+
228
+ end
229
+
230
+ end
231
+
@@ -0,0 +1,117 @@
1
+ #--
2
+ # Customize the standard Postgresql driver objects to make them
3
+ # more compatible with Og.
4
+ #++
5
+
6
+ class PGconn # :nodoc: all
7
+
8
+ # Redirect notices from backend to the logger instead of stderr
9
+
10
+ alias_method :initialize_pre_on_notice, :initialize
11
+ def initialize(*args)
12
+ initialize_pre_on_notice(*args)
13
+
14
+ on_notice { |message| Logger.info message }
15
+ end
16
+
17
+ # Check for older versions of ruby-postgres and postgres-pr
18
+
19
+ unless PGconn.instance_methods.include?('server_version')
20
+
21
+ # call-seq:
22
+ # conn.server_version -> Integer
23
+ #
24
+ # The number is formed by converting the major, minor, and revision numbers
25
+ # into two-decimal-digit numbers and appending them together. For example,
26
+ # version 7.4.2 will be returned as 70402, and version 8.1 will be returned
27
+ # as 80100 (leading zeroes are not shown). Zero is returned if the
28
+ # connection is bad.
29
+ # See also parse_version.
30
+
31
+ def server_version
32
+ @server_version ||= parse_version(exec('SHOW server_version').first_value)
33
+ rescue PGError => e
34
+ return 0
35
+ end
36
+
37
+ def parse_version(version)
38
+ version.split('.').map{|v| v.rjust(2,'0') }.join.to_i
39
+ end
40
+ private :parse_version
41
+
42
+ end
43
+
44
+ end
45
+
46
+
47
+ class PGresult # :nodoc: all
48
+ def blank?
49
+ 0 == num_tuples
50
+ end
51
+
52
+ def next
53
+ @row_idx ||= -1
54
+ result[@row_idx += 1]
55
+ end
56
+
57
+ def each_row
58
+ result.each_with_index do |row, idx|
59
+ yield(row, idx)
60
+ end
61
+ end
62
+
63
+ def first_value
64
+ val = getvalue(0, 0)
65
+ clear
66
+ return val
67
+ end
68
+
69
+ alias_method :close, :clear
70
+
71
+ end
72
+
73
+ class PGError # :nodoc: all
74
+ attr_accessor :og_info
75
+ end
76
+
77
+
78
+ # Compatibility layer for postgres-pr
79
+
80
+ if defined?(PostgresPR)
81
+
82
+ class PGconn # :nodoc: all
83
+ alias_method :query_pre_resque, :query
84
+ alias_method :exec_old, :exec
85
+
86
+ def query(*args)
87
+ exec_old(*args)
88
+ # postgres-pr raises NoMethodError when querying if no conn is available
89
+ rescue RuntimeError, NoMethodError => e
90
+ raise PGError, e.message
91
+ end
92
+
93
+ alias_method :exec, :query
94
+
95
+ class << self
96
+ alias_method :new_pre_resque, :new
97
+
98
+ def new(*args)
99
+ new_pre_resque(*args)
100
+ rescue RuntimeError => e
101
+ raise PGError, e.message
102
+ end
103
+
104
+ alias_method :connect, :new
105
+ end
106
+
107
+ def on_notice(&block)
108
+ @notice_processor = block
109
+ end
110
+
111
+ end
112
+
113
+ class PGError # :nodoc: all
114
+ alias error message
115
+ end
116
+
117
+ end
@@ -0,0 +1,15 @@
1
+ # Helper for mysql scripts.
2
+ #
3
+ # === Example
4
+ #
5
+ # mysql '-u root -p', %{
6
+ # drop database if exists weblog_development;
7
+ # create database weblog_development;
8
+ # grant all on weblog_development.* to #{`id -un`.strip}@localhost;
9
+ # }
10
+
11
+ def psql(opts, stream)
12
+ IO.popen("psql #{opts}", 'w') do |io|
13
+ io.puts stream
14
+ end
15
+ end