og 0.26.0 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.
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