og 0.26.0 → 0.27.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 (52) hide show
  1. data/INSTALL +10 -5
  2. data/ProjectInfo +9 -2
  3. data/README +3 -3
  4. data/Rakefile +5 -1
  5. data/doc/AUTHORS +3 -0
  6. data/doc/RELEASES +57 -0
  7. data/lib/glue/orderable.rb +8 -8
  8. data/lib/og.rb +6 -6
  9. data/lib/og/collection.rb +12 -3
  10. data/lib/og/entity.rb +2 -2
  11. data/lib/og/manager.rb +2 -1
  12. data/lib/og/store.rb +5 -8
  13. data/lib/og/store/kirby.rb +93 -12
  14. data/lib/og/store/mysql.rb +7 -21
  15. data/lib/og/store/psql.rb +9 -3
  16. data/lib/og/store/sql.rb +4 -2
  17. data/lib/og/store/sqlite.rb +3 -1
  18. data/lib/og/vendor/mysql.rb +1 -1
  19. data/setup.rb +1585 -0
  20. data/test/og/mixin/tc_hierarchical.rb +2 -0
  21. data/test/og/mixin/tc_optimistic_locking.rb +2 -0
  22. data/test/og/mixin/tc_orderable.rb +2 -0
  23. data/test/og/mixin/tc_taggable.rb +2 -0
  24. data/test/og/mixin/tc_timestamped.rb +2 -0
  25. data/test/og/store/tc_filesys.rb +2 -0
  26. data/test/og/store/tc_kirby.rb +2 -0
  27. data/test/og/tc_accumulator.rb +92 -0
  28. data/test/og/tc_delete_all.rb +2 -0
  29. data/test/og/tc_finder.rb +2 -0
  30. data/test/og/tc_inheritance.rb +2 -0
  31. data/test/og/tc_inheritance2.rb +35 -0
  32. data/test/og/tc_join.rb +2 -0
  33. data/test/og/tc_multi_validations.rb +19 -0
  34. data/test/og/tc_multiple.rb +2 -0
  35. data/test/og/tc_override.rb +2 -0
  36. data/test/og/tc_polymorphic.rb +3 -1
  37. data/test/og/tc_relation.rb +2 -0
  38. data/test/og/tc_reverse.rb +2 -0
  39. data/test/og/tc_scoped.rb +2 -0
  40. data/test/og/tc_select.rb +3 -0
  41. data/test/og/tc_store.rb +2 -0
  42. data/test/og/tc_types.rb +2 -0
  43. data/test/og/tc_validation.rb +2 -0
  44. data/test/og/tc_validation2.rb +207 -0
  45. data/test/og/tc_validation_loop.rb +36 -0
  46. metadata +124 -120
  47. data/benchmark/bench.rb +0 -75
  48. data/benchmark/sqlite-no-prepare.1.txt +0 -13
  49. data/benchmark/sqlite-no-prepare.2.txt +0 -13
  50. data/benchmark/sqlite-prepare.1.txt +0 -13
  51. data/benchmark/sqlite-prepare.2.txt +0 -13
  52. data/install.rb +0 -37
data/INSTALL CHANGED
@@ -40,16 +40,21 @@ a standard installation script is provided.
40
40
 
41
41
  2. Run the installation script.
42
42
 
43
- $ ruby install.rb
43
+ $ ruby setup.rb
44
44
 
45
45
  This installation script also installs some vendor libraries
46
46
  that you possibly have allready installed. Use with caution.
47
47
 
48
- You also have to manualy install the following libraries:
48
+ Dependencies needed: See 'ProjectInfo' file located in the
49
+ current directory.
50
+
51
+ Install the desired database client libraries Libraries are not
52
+ required for Og to run. If you wish to use an adapter which
53
+ uses one of the databases, then you'll need to install the
54
+ client library
55
+
56
+ Postgres, MySQL, Kirbybase
49
57
 
50
- * Nano
51
- * Mega
52
- * RedCloth
53
58
 
54
59
  = Manual installation.
55
60
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  TITLE : &title Og
4
4
  NAME : &pkg og
5
- VERSION : '0.26.0'
5
+ VERSION : '0.27.0'
6
6
  STATUS : beta
7
7
 
8
8
  AUTHOR : George Moschovitis
@@ -17,7 +17,7 @@ DESCRIPTION: >
17
17
  KirbyBase, Filesystem and more.
18
18
 
19
19
  DEPENDENCIES:
20
- - [ glue, '= 0.26.0' ]
20
+ - [ glue, '= 0.27.0' ]
21
21
 
22
22
  DISTRIBUTE: [ gem, tgz, zip ]
23
23
 
@@ -25,6 +25,13 @@ RUBYFORGE:
25
25
  PROJECT: 'nitro'
26
26
  USERNAME: 'gmosx'
27
27
 
28
+ RDOC:
29
+ - dir: rdoc
30
+ options: ['--all', '--inline-source']
31
+ include:
32
+ - 'lib/og/**/*'
33
+ - '[A-Z]*'
34
+
28
35
  # Anything to require upfront?
29
36
  #TEST:
30
37
  # fixture: ''
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = Og 0.26.0 README
1
+ = Og 0.27.0 README
2
2
 
3
3
  Og (ObjectGraph) is a powerful and elegant object-relational mapping
4
4
  library. Og manages the lifecycle of Ruby objects and provides
@@ -163,8 +163,8 @@ http://rubyforge.org/mailman/listinfo/nitro-general
163
163
 
164
164
  == Licence
165
165
 
166
- Copyright (c) 2004-2005, Navel Ltd (http://www.navel.gr)
167
- Copyright (c) 2004-2005, George 'gmosx' Moschovitis (http://www.gmosx.com).
166
+ Copyright (c) 2004-2006, George 'gmosx' Moschovitis (http://www.gmosx.com).
167
+ Copyright (c) 2004-2006, Navel Ltd (http://www.navel.gr)
168
168
 
169
169
  Og (http://www.nitrohq.com) is copyrighted free
170
170
  software created and maintained by George Moschovitis (mailto:gm@navel.gr)
data/Rakefile CHANGED
@@ -30,7 +30,11 @@ task :default => :package
30
30
  # Run the tests.
31
31
 
32
32
  Rake::TestTask.new do |t|
33
+ cwd = File.expand_path(File.join(File.dirname(__FILE__) + '.'))
33
34
  t.libs << 'test'
35
+ %w[glue nitro og].each do |dir|
36
+ t.libs << File.join(cwd, dir, 'lib')
37
+ end
34
38
  t.test_files = FileList['test/**/tc*.rb']
35
39
  t.verbose = true
36
40
  end
@@ -65,7 +69,7 @@ spec = Gem::Specification.new do |s|
65
69
  s.required_ruby_version = '= 1.8.3'
66
70
 
67
71
  s.files = FileList[
68
- '[A-Z]*', 'install.rb', '{bin,benchmark,examples,doc,lib,test,vendor}/**/*'
72
+ '[A-Z]*', 'setup.rb', '{bin,benchmark,examples,doc,lib,test,vendor}/**/*'
69
73
  ].exclude("_darcs").exclude("_darcs/**/*").exclude('**/*.log').to_a
70
74
 
71
75
  s.require_path = 'lib'
@@ -33,5 +33,8 @@ IDEAS, ADDITIONAL CODING, SUPPORT:
33
33
  * Matt Bowen <matt.bowen@farweststeel.com>
34
34
  Oracle driver, documentation.
35
35
 
36
+ * Aidan Rogers <aidan@yoyo.org>
37
+ Bug reports, patches.
38
+
36
39
  * Thomas Quas <tquas@yahoo.com>
37
40
  Ideas, bug reports, unit tests.
@@ -1,3 +1,60 @@
1
+ == Version 0.27.0
2
+
3
+ Once again we have a great mix of cool new features, along
4
+ with bugfixes and a myriad of smaller improvements. Go and
5
+ download the most advanced Ruby ORM Framework you can find.
6
+
7
+ Most notable changes:
8
+
9
+ * Og live collections support accumulation. Here is an example:
10
+
11
+ class Category
12
+ has_many :projects
13
+ end
14
+
15
+ class Project
16
+ has_many :clients
17
+ end
18
+
19
+ class Client
20
+ end
21
+
22
+ clients = category.projects.clients
23
+
24
+ # => returns all clients for this category!
25
+
26
+ * New WebFile system. Uploading files and handling photos was
27
+ never easier:
28
+
29
+ class Photo
30
+ is Timestamped
31
+ is Taggable
32
+ property :title, String
33
+ property :file, WebFile, :magick => { :small => '64x64', :medium => '128x128' }
34
+ end
35
+
36
+ # the upload action
37
+
38
+ def upload
39
+ photo = Photo.assign(request)
40
+ photo.save
41
+ end
42
+
43
+ This saves the photo, and creates 2 thumbnails. You can easily
44
+ access the photo and thumbnails like this:
45
+
46
+ <img src="#{photo.file_medium_thumbnail}" />
47
+ <img src="#{photo.file_small_thumbnail}" />
48
+
49
+ ie obj.{propertyname}_#{thumbname}_thumbnail
50
+
51
+ * Improved the generated RDOC comments.
52
+
53
+ * Added evolution support to the KirbyBase adapter.
54
+
55
+ * Added setup.rb for non-gem installation.
56
+
57
+
1
58
  == Version 0.26.0
2
59
 
3
60
  This is the release with the most community contributions. Check
@@ -128,7 +128,7 @@ module Orderable
128
128
  end
129
129
  self.class.update( adj, :condition => con.join(' AND ') )
130
130
  @position = dest_position
131
- update_property(:"#{pos}")
131
+ update_property(pos.to_sym)
132
132
  end
133
133
 
134
134
  self
@@ -185,12 +185,12 @@ module Orderable
185
185
 
186
186
  def increment_position
187
187
  @position += 1
188
- update_property(:"#{orderable_position}")
188
+ update_property(orderable_position.to_sym)
189
189
  end
190
190
 
191
191
  def decrement_position
192
192
  @position -= 1
193
- update_property(:"#{orderable_position}")
193
+ update_property(orderable_position.to_sym)
194
194
  end
195
195
 
196
196
  def bottom_position
@@ -200,31 +200,31 @@ module Orderable
200
200
 
201
201
  def set_top_position
202
202
  @position = 1
203
- update_property(:"#{orderable_position}")
203
+ update_property(orderable_position.to_sym)
204
204
  end
205
205
 
206
206
  def set_bottom_position
207
207
  @position = bottom_position + 1
208
- update_property(:"#{orderable_position}")
208
+ update_property(orderable_position.to_sym)
209
209
  end
210
210
 
211
211
  def increment_position_of_higher_items
212
212
  pos = orderable_position
213
213
  con = orderable_condition + [ "#{pos} < #{@position}" ]
214
- self.class.update( "#{pos}=(#{pos} + 1)", :condition => con.join(' AND ') )
214
+ self.class.update "#{pos}=(#{pos} + 1)", :condition => con.join(' AND ')
215
215
  end
216
216
 
217
217
  def increment_position_of_all_items
218
218
  pos = orderable_position
219
219
  con = orderable_condition
220
220
  con = con.empty? ? nil : con.join(' AND ')
221
- self.class.update("#{pos}=(#{pos} + 1)", :condition => con )
221
+ self.class.update "#{pos}=(#{pos} + 1)", :condition => con
222
222
  end
223
223
 
224
224
  def decrement_position_of_lower_items
225
225
  pos = orderable_position
226
226
  con = orderable_condition + [ "#{pos} > #{@position}" ]
227
- self.class.update( "#{pos}=(#{pos} - 1)", :condition => con.join(' AND ') )
227
+ self.class.update "#{pos}=(#{pos} - 1)", :condition => con.join(' AND ')
228
228
  end
229
229
 
230
230
  end
data/lib/og.rb CHANGED
@@ -43,7 +43,7 @@ module Og
43
43
 
44
44
  # The version.
45
45
 
46
- Version = '0.26.0'
46
+ Version = '0.27.0'
47
47
 
48
48
  # Library path.
49
49
 
@@ -115,16 +115,16 @@ module Og
115
115
  # if it was being called by Og.setup to provide
116
116
  # additional, faster or enhanced functionality.
117
117
 
118
- puts options[:called_by_og_setup]
119
118
  options[:called_by_og_setup] = true if options[:called_by_og_setup].nil?
120
119
 
121
120
  m = @@manager = Manager.new(options)
122
121
  m.manage_classes
123
122
 
124
- # Allows functionality that requires a store is finalized
125
- # to be implemented. A vastly superior method of constructing
126
- # foreign key constraints is an example of functionality
127
- # this provides. Currently only used by the PostgreSQL store.
123
+ # Allows functionality that requires a store is
124
+ # finalized to be implemented. A vastly superior
125
+ # method of constructing foreign key constraints is an
126
+ # example of functionality this provides. Currently
127
+ # only used by the PostgreSQL store.
128
128
 
129
129
  m.post_setup if options[:called_by_og_setup]
130
130
  rescue Exception => ex
@@ -228,11 +228,20 @@ class Collection
228
228
  find(options).first
229
229
  end
230
230
 
231
- # Redirect all other methods to the members array.
231
+ # Try to execute an accumulator or else
232
+ # redirect all other methods to the members array.
233
+ #
234
+ # An example of the accumulator:
235
+ #
236
+ # foo_foobars = foo1.bars.foobars
232
237
 
233
238
  def method_missing(symbol, *args, &block)
234
239
  load_members
235
- @members.send(symbol, *args, &block)
240
+ if @member_class.instance_methods.include? symbol.to_s
241
+ @members.inject([]) { |a, x| a << x.send(symbol) }.flatten
242
+ else
243
+ @members.send(symbol, *args, &block)
244
+ end
236
245
  end
237
246
 
238
247
  private
@@ -245,4 +254,4 @@ end
245
254
  end
246
255
 
247
256
  # * George Moschovitis <gm@navel.gr>
248
- # * Julien Perrot <jperrot@exosec.fr>
257
+ # * Julien Perrot <jperrot@exosec.fr>
@@ -106,8 +106,8 @@ module EntityMixin
106
106
  return obj
107
107
  end
108
108
 
109
- def assign_properties(values, options)
110
- Property.fill(self.new, values, options)
109
+ def assign_properties(values, options = {})
110
+ Property.populate_object(self.new, values, options)
111
111
  end
112
112
  alias_method :assign, :assign_properties
113
113
 
@@ -102,9 +102,10 @@ class Manager
102
102
  info = EntityInfo.new(klass)
103
103
 
104
104
  # DON'T DO THIS!!!
105
+
105
106
  klass.module_eval %{
106
107
  def ==(other)
107
- other ? @#{klass.primary_key.symbol} == other.#{klass.primary_key.symbol} : false
108
+ other.instance_of?(#{klass}) ? @#{klass.primary_key.symbol} == other.#{klass.primary_key.symbol} : false
108
109
  end
109
110
  }
110
111
 
@@ -18,12 +18,9 @@ class Store
18
18
 
19
19
  def self.for_name(name)
20
20
  Logger.info "Og uses the #{name.to_s.capitalize} store."
21
-
22
21
  # gmosx: to keep RDoc happy.
23
- eval %{
24
- require 'og/store/#{name}'
25
- return #{name.to_s.capitalize}Store
26
- }
22
+ require('og/store/' + name.to_s)
23
+ return Og.const_get("#{name.to_s.capitalize}Store")
27
24
  end
28
25
 
29
26
  # Creates a store.
@@ -192,10 +189,8 @@ class Store
192
189
  yield(self)
193
190
  commit
194
191
  rescue => ex
195
- Logger.error 'Error in transaction'
196
- Logger.error ex
197
- Logger.error ex.backtrace
198
192
  rollback
193
+ raise ex
199
194
  end
200
195
  end
201
196
 
@@ -218,3 +213,5 @@ private
218
213
  end
219
214
 
220
215
  end
216
+
217
+ # * George Moschovitis <gm@navel.gr>
@@ -34,6 +34,12 @@ class KirbyStore < SqlStore
34
34
 
35
35
  def initialize(options)
36
36
  super
37
+
38
+ @typemap = {
39
+ :Fixnum => :Integer,
40
+ :TrueClass => :Boolean
41
+ }
42
+
37
43
  mode = options[:mode] || :local
38
44
 
39
45
  if mode == :client
@@ -62,10 +68,14 @@ class KirbyStore < SqlStore
62
68
  klass.send :alias_method, :recno, :oid
63
69
  klass.send :alias_method, :recno=, :oid=
64
70
 
71
+ unless klass.properties.include? :recno
72
+ klass.property :recno, Fixnum
73
+ end
74
+
65
75
  symbols = klass.properties.keys
66
76
 
67
77
  klass.module_eval %{
68
- def self.kb_create(recno, #{symbols.join(', ')})
78
+ def self.kb_create(#{symbols.join(', ')})
69
79
  obj = self.allocate
70
80
  obj.recno = recno
71
81
  #{ symbols.map { |s| "obj.#{s} = #{s}; "} }
@@ -158,29 +168,101 @@ class KirbyStore < SqlStore
158
168
  # nop, not supported.
159
169
  end
160
170
 
171
+ def join(obj1, obj2, table, options = nil)
172
+ first, second = join_object_ordering(obj1, obj2)
173
+ @conn.get_table(table.to_sym).insert(first.pk, second.pk)
174
+ end
175
+
176
+ def unjoin(obj1, obj2, table)
177
+ first, second = join_object_ordering(obj1, obj2)
178
+
179
+ @conn.get_table(table.to_sym).delete do |r|
180
+ require 'dev-utils/debug'
181
+ breakpoint
182
+ r.send(:first_key) == first.pk and
183
+ r.send(:second_key) == second.pk
184
+ end
185
+ end
186
+
161
187
  private
162
188
 
189
+ def typemap(key)
190
+ @typemap[key] || key
191
+ end
192
+
163
193
  def create_table(klass)
164
- fields = fields_for_class(klass)
165
- begin
194
+ if @conn.table_exists?(klass.table.to_sym)
195
+ get_table(klass).pack # Kirby specific method of database cleanup.
196
+
197
+ field_names = field_names_for_class(klass)
198
+ actual_fields = get_table(klass).field_names
199
+
200
+ field_names.each do |needed_field|
201
+ next if actual_fields.include?(needed_field)
202
+
203
+ if @options[:evolve_schema] == true
204
+ Logger.debug "Adding field '#{needed_field}' to '#{klass.table}'"
205
+ field_type = typemap(klass.properties[needed_field].klass.name.to_sym)
206
+ if get_table(klass).respond_to?(:add_column)
207
+ get_table(klass).add_column(needed_field, field_type)
208
+ else
209
+ @conn.add_table_column(klass.table, needed_field, field_type)
210
+ end
211
+ else
212
+ Logger.warn "Table '#{klass.table}' is missing field '#{needed_field}' and :evolve_schema is not set to true!"
213
+ end
214
+ end
215
+
216
+ actual_fields.each do |obsolete_field|
217
+ next if field_names.include?(obsolete_field)
218
+
219
+ if @options[:evolve_schema] == true and @options[:evolve_schema_cautious] == false
220
+ Logger.debug "Removing obsolete field '#{obsolete_field}' from '#{klass.table}'"
221
+ if get_table(klass).respond_to?(:drop_column)
222
+ get_table(klass).drop_column(obsolete_field)
223
+ else
224
+ @conn.drop_table_column(klass.table, obsolete_field)
225
+ end
226
+ else
227
+ Logger.warn "You have an obsolete field '#{obsolete_field}' on table '#{klass.table}' and :evolve_schema is not set or is in cautious mode!"
228
+ end
229
+ end
230
+ else
231
+ Logger.debug "Creating table '#{klass.table}'"
232
+ fields = fields_for_class(klass)
166
233
  table = @conn.create_table(klass.table.to_sym, *fields) do |t|
167
234
  t.record_class = klass
168
235
  end
169
- rescue Object => ex
170
- # gmosx: any idea how to better test this?
171
- if ex.to_s =~ /already exists/i
172
- Logger.debug "Table for '#{klass}' already exists!"
173
- return
174
- else
175
- raise
236
+ end
237
+
238
+ =begin
239
+ # Create join tables if needed. Join tables are used in
240
+ # 'many_to_many' relations.
241
+
242
+ if join_tables = klass.ann.self[:join_tables]
243
+ for info in join_tables
244
+ unless @conn.table_exists?(info[:table].to_sym)
245
+ @conn.create_table(info[:table].to_sym, *create_join_table_sql(info))
246
+ Logger.debug "Created jointable '#{info[:table]}'."
247
+ end
176
248
  end
177
249
  end
250
+ =end
251
+ end
252
+
253
+
254
+ def create_join_table_sql(join_info)
255
+ [join_info[:first_key].to_sym, :Integer, join_info[:second_key].to_sym, :Integer]
178
256
  end
179
257
 
180
258
  def drop_table(klass)
181
259
  @conn.drop_table(klass.table) if @conn.table_exists?(klass.table)
182
260
  end
183
261
 
262
+ def field_names_for_class(klass)
263
+ klass.properties.values.map {|p| p.symbol }
264
+ end
265
+
184
266
  def fields_for_class(klass)
185
267
  fields = []
186
268
 
@@ -189,8 +271,7 @@ private
189
271
 
190
272
  fields << p.symbol
191
273
 
192
- type = p.klass.name.to_sym
193
- type = :Integer if type == :Fixnum
274
+ type = typemap(p.klass.name.to_sym)
194
275
 
195
276
  fields << type
196
277
  end