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