sam-dm-core 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,145 +1,16 @@
1
+ === 0.9.8 / 2008-12-07
1
2
 
2
- == 0.3.0
3
- * HasManyAssociation::Set now has a nil? method, so we can do stuff like cage.animal.nil?
3
+ * 3 minor enhancements:
4
4
 
5
- == 0.2.5
6
- * has_one bugfixes
7
- * Added syntax for setting CHECK-constraints directly in your properties (Postgres)
8
- * You can now set indexes with :index => true and :index => :unique
9
- * Support for composite indexes (thanks to Jeffrey Gelens)
10
- * Add composite scope to validates_uniqueness
11
- * Added private/protected properties
12
- * Remove HasOneAssociation, Make HasManyAssociation impersonate has_one relationships
13
- * Added #get method
14
- * Persistence module added, inheriting from DataMapper::Base no longer necessary
5
+ * Updated Resource#inspect to not lazy-load unloaded attributes.
6
+ * Updated ManyToOne::Proxy to delegate #class to parent. This will
7
+ allow the Merb resource() route helper method to use the association
8
+ proxy to generate the URL.
9
+ * Updated Resource#attributes= to respect method visibility. This means
10
+ that only public mutators will set the attribute value.
15
11
 
16
- == 0.2.4
17
- * Bug fixes
18
- * Added paranoia
12
+ * 1 bug fix:
19
13
 
20
- == 0.2.3
21
- * Added String#t for translation and overrides for default validation messages
22
- * Give credit where it's due: zapnap, not pimpmaster, submitted the String#blank? patch. My bad. :-(
23
- * MAJOR: Resolve issue with non-unique-hash values and #dirty?; now frozen original values are stored instead
24
- * Added Base#update_attributes
25
- * MAJOR: Queries are now passed to the database drivers in a parameterized fashion
26
- * Updated PostgreSQL driver and adapter to current
27
-
28
- == 0.2.2
29
- * Removed C extension bundles and log files from package
30
-
31
- == 0.2.1
32
- * Added :float column support
33
- * Added association proxies: ie: Zoo.first.exhibits.animals
34
- * Columns stored in SortedSet
35
- * Swig files are no longer RDOCed
36
- * Added :date column support
37
- * BUG: Fixed UTC issues with datetimes
38
- * Added #to_yaml method
39
- * Added #to_xml method
40
- * Added #to_json method
41
- * BUG: Fixed HasManyAssociation::Set#inspect
42
- * BUG: Fixed #reload!
43
- * BUG: Column copy for STI moved into Table#initialize to better handle STI with multiple mapped databases
44
- * BUG: before_create callbacks moved in the execution flow since they weren't guaranteed to fire before
45
- * Threading enhancements: Removed single_threaded_mode, #database block form adjusted for thread-safety
46
- * BUG: Fixed String#blank? when a multi-line string contained a blank line (thanks zapnap!)
47
- * Performance enhancements: (thanks wycats!)
48
-
49
- == 0.2.0
50
- * AdvancedHasManyAssociation now functional for fetches
51
- * AdvancedHasManyAssociation renamed to HasNAssociation
52
- * HasManyAssociation refactored to use HasNAssociation superclass
53
- * Slight spec tweaks to accomodate the updates
54
- * HasOneAssociation refactored to use HasNAssociation superclass
55
- * Added HasAndBelongsToManyAssociation, using HasNAssociation as a basis; Need to add corresponding SQL generation code in AdvancedLoadCommand
56
- * Added spec for habtm query generation
57
- * HasNAssociation#foreign_key returns a DataMapper::Adapters::Sql::Mappings::Column instance instead of a raw String now
58
- * Added table, association, association_table and to_sql methods to HasNAssociation
59
- * Added associations_spec.rb
60
- * Added a forced table-recreation to spec_helper.rb so the tests could run with a clean version of the database, including any new columns added to the models
61
- * Added HasAndBelongsToManyAssociation#to_sql (all current specs pass now!)
62
- * Minor tweaks to Callbacks
63
- * Added CallbacksHelper to declare class-method ::callbacks on DataMapper::Base
64
- * Implemented before_validate and after_validate hooks in ValidationHelper
65
- * Minor documentation additions in callbacks.rb
66
- * Added callbacks_spec
67
- * Moved class-method declarations for built-in callbacks to the callbacks helper instead of DataMapper::Base
68
- * Renamed :before/after_validate callback to :before/after_validation to match ActiveRecord
69
- * Callbacks#add now accepts a Symbol which maps a callback to a method call on the targetted instance, also added a spec to verify this behavior
70
- * Documented callbacks.rb
71
- * Added DataMapper::Associations::Reference class
72
- * Documented DataMapper::Associations::Reference class
73
- * Upgraded BelongsToAssociation to new style
74
- * Added AssociationsSet to handle simple "last-in" for association bindings
75
- * Fixed extra spec loading
76
- * Added *Association#columns
77
- * Some refactoring in AdvancedLoadCommand regarding :include options
78
- * Added support for class-less Mappings::Table instances, with just a string name
79
- * HasAndBelongsToManyAssociation#join_table #left_foreign_key and #right_foreign_key reference actual Table or Column objects now
80
- * Added :shallow_include option for HABTM joins in AdvancedLoadCommand and corresponding spec
81
- * Added Commands::AdvancedConditions
82
- * Added ORDER, LIMIT, OFFSET and WHERE support to AdvancedLoadCommand
83
- * Renamed spec/has_many.rb to spec/has_many_spec.rb
84
- * Tweaked the loading of has_many relationships; big performance boost; got rid of an extra query
85
- * Added EmbeddedValue support, and accompanying spec
86
- * Fleshed out AdvancedConditions a bit; added conditions_spec.rb
87
- * Added more AdvancedConditions specs
88
- * Added Loader to handle multi-instanced rows
89
- * AdvancedLoadCommand replaced LoadCommand; down to 3 failing specs
90
- * All specs pass
91
- * Added :intercept_load finder option and accompanying spec
92
- * Modified :intercept_load block signature to |instance,columns,row|
93
- * HasAndBelongsToMany works, all specs pass
94
- * Fixed a couple bugs with keys; Added DataMapper::Base#key= method
95
- * Made DataMapper::Base#lazy_load! a little more flexible
96
- * Removed LoadCommand overwrites from MysqlAdapter
97
- * Default Database#single_threaded mode is true now
98
- * Removed MysqlAdapter#initialize, which only served to setup the connections, moved to SqlAdapter
99
- * Added SqlAdapter#create_connection and SqlAdapter#close_connection abstract methods
100
- * Added MysqlAdapter#create_connection and MysqlAdapter#close_connection concrete methods
101
- * Made SqlAdapter#connection a concrete method (instead of abstract), with support for single_threaded operation
102
- * Database#setup now takes a Hash of options instead of a block-initializer
103
- * Validation chaining should work for all association types
104
- * Save chaining should work for has_many associations
105
- * Added benchmarks for in-session performance to performance.rb
106
- * Removed block conditions; They're slower and don't offer any real advantages
107
- * Removed DeleteCommand
108
- * Removed SaveCommand
109
- * Removed TableExistsCommand
110
- * Session renamed to Context
111
- * Most command implementations moved to methods in SqlAdapter
112
- * Removed UnitOfWork module, instead moving a slightly refactored implementation into Base
113
-
114
- == 0.1.1
115
- * Removed /lib/data_mapper/extensions
116
- * Moved ActiveRecordImpersonation into DataMapper::Support module
117
- * Moved CallbackHelper methods into DataMapper::Base class
118
- * Moved ValidationHelper into DataMapper::Validations module
119
- * Removed LoadedSet since it's not necessary for it to reference the Database, so it's nothing more than an array now; Replaced with Array
120
- * Modified data_mapper.rb to load DataMapper::Support::Enumerable
121
- * Modified example.rb and performance.rb to require 'lib/data_mapper' instead of modifying $LOADPATH
122
- * Created SqlAdapter base-class
123
- * Refactored MysqlAdapter to use SqlAdapter superclass
124
- * Refactored Sqlite3Adapter to use SqlAdapter superclass
125
- * Moved /lib/data_mapper/queries to /lib/data_mapper/adapters/sql/queries
126
- * Moved Connection, Result and Reader classes along with Coersion and Quoting modules to DataMapper::Adapters::Sql module
127
- * Moved DataMapper::Adapters::Sql::Queries to ::Commands
128
- * Moved Mappings to SqlAdapter
129
- * Added monolithic DeleteCommand
130
- * Added monolithic SaveCommand
131
- * Added TableExistsCommand
132
- * Moved save/delete logic out of Session
133
- * Added create-table functionality to SaveCommand
134
- * Cleaned up Session; #find no longer supported, use #all or #first
135
- * Moved object materialization into LoadCommand
136
- * Migrated Sqlite3Adapter::Commands
137
- * Added Session#query support back in
138
- * Removed Connection/Reader/Result classes
139
- * Set DataMapper::Base#key on load to avoid double-hit against Schema
140
- * Added DataMapper::Support::Struct for increased Session#query performance
141
- * Added AdvancedHasManyAssociation (preview status)
142
- * Added benchmarks comparing ActiveRecord::Base::find_by_sql with Session#query
143
-
144
- == 0.1.0
145
- * Initial Public Release
14
+ * Fixed regression where an update to a resource with a lazy property
15
+ could cause the record to reload when accessing the lazy property,
16
+ clobbering any changes made prior.
data/Rakefile CHANGED
@@ -13,7 +13,7 @@ AUTHOR = "Sam Smoot"
13
13
  EMAIL = "ssmoot@gmail.com"
14
14
  GEM_NAME = "dm-core"
15
15
  GEM_VERSION = DataMapper::VERSION
16
- GEM_DEPENDENCIES = ["data_objects", "~>0.9.9"],
16
+ GEM_DEPENDENCIES = ["data_objects", "~>0.9.10"],
17
17
  ["extlib", "~>0.9.9"],
18
18
  ["addressable", "~>2.0.1"]
19
19
 
@@ -1,4 +1,4 @@
1
- gem 'data_objects', '~>0.9.9'
1
+ gem 'data_objects', '~>0.9.10'
2
2
  require 'data_objects'
3
3
 
4
4
  module DataMapper
@@ -1,4 +1,4 @@
1
- gem 'do_mysql', '~>0.9.9'
1
+ gem 'do_mysql', '~>0.9.10'
2
2
  require 'do_mysql'
3
3
 
4
4
  module DataMapper
@@ -1,4 +1,4 @@
1
- gem 'do_postgres', '~>0.9.9'
1
+ gem 'do_postgres', '~>0.9.10'
2
2
  require 'do_postgres'
3
3
 
4
4
  module DataMapper
@@ -1,4 +1,4 @@
1
- gem 'do_sqlite3', '~>0.9.9'
1
+ gem 'do_sqlite3', '~>0.9.10'
2
2
  require 'do_sqlite3'
3
3
 
4
4
  module DataMapper
@@ -494,6 +494,32 @@ module DataMapper
494
494
  super || model.public_methods(false).include?(method.to_s) || relationships.has_key?(method)
495
495
  end
496
496
 
497
+ # TODO: add docs
498
+ # @api private
499
+ def _dump(*)
500
+ Marshal.dump([ query, to_a ])
501
+ end
502
+
503
+ # TODO: add docs
504
+ # @api private
505
+ def self._load(marshalled)
506
+ query, array = Marshal.load(marshalled)
507
+
508
+ # XXX: IMHO it is a code smell to be forced to use allocate
509
+ # and instance_variable_set to load an object. You should
510
+ # be able to use a constructor to provide all the info needed
511
+ # to initialize an object. This should be fixed in the edge
512
+ # branch dkubb/dm-core
513
+
514
+ collection = allocate
515
+ collection.instance_variable_set(:@query, query)
516
+ collection.instance_variable_set(:@array, array)
517
+ collection.instance_variable_set(:@loaded, true)
518
+ collection.instance_variable_set(:@key_properties, collection.send(:model).key(collection.repository.name))
519
+ collection.instance_variable_set(:@cache, {})
520
+ collection
521
+ end
522
+
497
523
  protected
498
524
 
499
525
  ##
data/lib/dm-core/model.rb CHANGED
@@ -306,11 +306,30 @@ module DataMapper
306
306
  resource
307
307
  end
308
308
 
309
- # TODO SPEC
309
+ ##
310
+ # Copy a set of records from one repository to another.
311
+ #
312
+ # @param [String] source
313
+ # The name of the Repository the resources should be copied _from_
314
+ # @param [String] destination
315
+ # The name of the Repository the resources should be copied _to_
316
+ # @param [Hash] query
317
+ # The conditions with which to find the records to copy. These
318
+ # conditions are merged with Model.query
319
+ #
320
+ # @return [DataMapper::Collection]
321
+ # A Collection of the Resource instances created in the operation
322
+ #
323
+ # @api public
310
324
  def copy(source, destination, query = {})
325
+
326
+ # get the list of properties that exist in the source and destination
327
+ destination_properties = properties(destination)
328
+ fields = query[:fields] ||= properties(source).select { |p| destination_properties.has_property?(p.name) }
329
+
311
330
  repository(destination) do
312
- repository(source).read_many(scoped_query(query)).each do |resource|
313
- self.create(resource.attributes)
331
+ all(query.merge(:repository => repository(source))).map do |resource|
332
+ create(fields.map { |p| [ p.name, p.get(resource) ] }.to_hash)
314
333
  end
315
334
  end
316
335
  end
@@ -366,6 +385,13 @@ module DataMapper
366
385
  Query.new(repository, self, query.merge(conditions))
367
386
  end
368
387
 
388
+ # TODO: add docs
389
+ # @api private
390
+ def _load(marshalled)
391
+ repository_name, attributes = Marshal.load(marshalled)
392
+ repository(repository_name) { new(attributes) }
393
+ end
394
+
369
395
  def typecast_key(key)
370
396
  self.key(repository_name).zip(key).map { |k, v| k.typecast(v) }
371
397
  end
@@ -512,6 +512,19 @@ module DataMapper
512
512
  "#<Property:#{@model}:#{@name}>"
513
513
  end
514
514
 
515
+ # TODO: add docs
516
+ # @api private
517
+ def _dump(*)
518
+ Marshal.dump([ repository, model, name ])
519
+ end
520
+
521
+ # TODO: add docs
522
+ # @api private
523
+ def self._load(marshalled)
524
+ repository, model, name = Marshal.load(marshalled)
525
+ model.properties(repository.name)[name]
526
+ end
527
+
515
528
  private
516
529
 
517
530
  def initialize(model, name, type, options = {})
data/lib/dm-core/query.rb CHANGED
@@ -151,6 +151,55 @@ module DataMapper
151
151
  "#<#{self.class.name} #{attrs.map { |(k,v)| "@#{k}=#{v.inspect}" } * ' '}>"
152
152
  end
153
153
 
154
+ # TODO: add docs
155
+ # @api public
156
+ def to_hash
157
+ hash = {
158
+ :reload => reload?,
159
+ :unique => unique?,
160
+ :offset => offset,
161
+ :order => order,
162
+ :add_reversed => add_reversed?,
163
+ :fields => fields,
164
+ }
165
+
166
+ hash[:limit] = limit unless limit == nil
167
+ hash[:links] = links unless links == []
168
+ hash[:includes] = includes unless includes == []
169
+
170
+ conditions = {}
171
+ raw_queries = []
172
+ bind_values = []
173
+
174
+ conditions.each do |condition|
175
+ if condition[0] == :raw
176
+ raw_queries << condition[1]
177
+ bind_values << condition[2]
178
+ else
179
+ operator, property, bind_value = condition
180
+ conditions[ Query::Operator.new(property, operator) ] = bind_value
181
+ end
182
+ end
183
+
184
+ if raw_queries.any?
185
+ hash[:conditions] = [ raw_queries.join(' ') ].concat(bind_values)
186
+ end
187
+
188
+ hash.update(conditions)
189
+ end
190
+
191
+ # TODO: add docs
192
+ # @api private
193
+ def _dump(*)
194
+ Marshal.dump([ repository, model, to_hash ])
195
+ end
196
+
197
+ # TODO: add docs
198
+ # @api private
199
+ def self._load(marshalled)
200
+ new(*Marshal.load(marshalled))
201
+ end
202
+
154
203
  private
155
204
 
156
205
  def initialize(repository, model, options = {})
@@ -91,6 +91,14 @@ module DataMapper
91
91
  "#<DataMapper::Repository:#{@name}>"
92
92
  end
93
93
 
94
+ def _dump(*)
95
+ name.to_s
96
+ end
97
+
98
+ def self._load(marshalled)
99
+ new(marshalled.to_sym)
100
+ end
101
+
94
102
  private
95
103
 
96
104
  def initialize(name)
@@ -528,6 +528,19 @@ module DataMapper
528
528
  model.to_query(repository, key, query) unless new_record?
529
529
  end
530
530
 
531
+ # TODO: add docs
532
+ # @api private
533
+ def _dump(*)
534
+ repository_name = repository.name
535
+ attributes = {}
536
+
537
+ model.properties(repository_name).slice(*loaded_attributes).compact.each do |property|
538
+ attributes[property.name] = property.get(self) if property.writer_visibility == :public
539
+ end
540
+
541
+ Marshal.dump([ repository_name, attributes ])
542
+ end
543
+
531
544
  protected
532
545
 
533
546
  def properties
@@ -1,3 +1,3 @@
1
1
  module DataMapper
2
- VERSION = '0.9.8' unless defined?(DataMapper::VERSION)
2
+ VERSION = '0.9.9' unless defined?(DataMapper::VERSION)
3
3
  end
@@ -127,6 +127,10 @@ if ADAPTER
127
127
  results.first.should == bob
128
128
  end
129
129
 
130
+ it 'should be serializable with Marshal' do
131
+ Marshal.load(Marshal.dump(Zebra.all)).should == Zebra.all
132
+ end
133
+
130
134
  describe 'model proxying' do
131
135
  it 'should delegate to a model method' do
132
136
  stripes = @model.first.stripes
@@ -123,5 +123,75 @@ if ADAPTER
123
123
  }.should_not raise_error(NoMethodError)
124
124
  end
125
125
  end
126
+
127
+ it { ModelSpec::STI.should respond_to(:copy) }
128
+
129
+ ([ :sqlite3, :mysql, :postgres ] - [ ADAPTER ]).each do |alternate|
130
+ describe '#copy' do
131
+ describe 'between identical models' do
132
+ before do
133
+ ModelSpec::STI.auto_migrate!(alternate)
134
+
135
+ ModelSpec::STI.create(:name => 'Record 1')
136
+ ModelSpec::STI.create(:name => 'Record 2')
137
+
138
+ # copy from the default to the alternate repository
139
+ @return = @resources = ModelSpec::STI.copy(:default, alternate)
140
+ end
141
+
142
+ it 'should return an Enumerable' do
143
+ @return.should be_a_kind_of(Enumerable)
144
+ end
145
+
146
+ it 'should return Resources' do
147
+ @return.each { |r| r.should be_a_kind_of(DataMapper::Resource) }
148
+ end
149
+
150
+ it 'should have each Resource set to the expected Repository' do
151
+ @resources.each { |r| r.repository.name.should == alternate }
152
+ end
153
+
154
+ it 'should create the Resources in the expected Repository' do
155
+ ModelSpec::STI.all(:repository => repository(alternate)).should == @resources
156
+ end
157
+ end
158
+
159
+ describe 'between different models' do
160
+ before do
161
+ # add an extra property to the alternate model
162
+ repository(alternate) do
163
+ ModelSpec::STI.property :status, String, :default => 'new'
164
+ end
165
+
166
+ ModelSpec::STI.auto_migrate!(alternate)
167
+
168
+ # add new resources to the alternate repository
169
+ repository(alternate) do
170
+ ModelSpec::STI.create(:name => 'Record 1')
171
+ ModelSpec::STI.create(:name => 'Record 2')
172
+ end
173
+
174
+ # copy from the alternate to the default repository
175
+ @return = @resources = ModelSpec::STI.copy(alternate, :default)
176
+ end
177
+
178
+ it 'should return an Enumerable' do
179
+ @return.should be_a_kind_of(Enumerable)
180
+ end
181
+
182
+ it 'should return Resources' do
183
+ @return.each { |r| r.should be_a_kind_of(DataMapper::Resource) }
184
+ end
185
+
186
+ it 'should have each Resource set to the expected Repository' do
187
+ @resources.each { |r| r.repository.name.should == :default }
188
+ end
189
+
190
+ it 'should create the Resources in the expected Repository' do
191
+ ModelSpec::STI.all.should == @resources
192
+ end
193
+ end
194
+ end
195
+ end
126
196
  end
127
197
  end
@@ -3,6 +3,22 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
3
3
  gem 'fastercsv', '~>1.4.0'
4
4
  require 'fastercsv'
5
5
 
6
+ describe DataMapper::Property do
7
+ before do
8
+ module PropertySpec
9
+ class Resource
10
+ include DataMapper::Resource
11
+ end
12
+ end
13
+
14
+ @property = PropertySpec::Resource.property :id, DM::Serial
15
+ end
16
+
17
+ it 'should be serializable with Marshal' do
18
+ Marshal.load(Marshal.dump(@property)).should == @property
19
+ end
20
+ end
21
+
6
22
  if ADAPTER
7
23
  describe DataMapper::Property, "with #{ADAPTER}" do
8
24
  describe " tracking strategies" do
@@ -125,6 +125,14 @@ if ADAPTER
125
125
  end
126
126
 
127
127
  describe DataMapper::Query, "with #{ADAPTER}" do
128
+ before do
129
+ @query = DataMapper::Query.new(repository(ADAPTER), QuerySpec::SailBoat)
130
+ end
131
+
132
+ it 'should be serializable with Marshal' do
133
+ Marshal.load(Marshal.dump(@query)).should == @query
134
+ end
135
+
128
136
  describe '#unique' do
129
137
  include LoggingHelper
130
138
 
@@ -137,7 +145,7 @@ if ADAPTER
137
145
  end
138
146
 
139
147
  def parse_statement(log)
140
- log.readlines.join.chomp.split(' ~ ').last
148
+ log.readlines.join.chomp.split(' ~ ').last.sub(/\A\(\d+\.\d+\)\s+/, '')
141
149
  end
142
150
 
143
151
  describe 'when true' do
@@ -21,6 +21,10 @@ if ADAPTER
21
21
  @query = DataMapper::Query.new(@repository, @model)
22
22
  end
23
23
 
24
+ it 'should be serializable with Marshal' do
25
+ Marshal.load(Marshal.dump(@repository)).should == @repository
26
+ end
27
+
24
28
  it "should throw an exception if the named repository is unknown" do
25
29
  r = DataMapper::Repository.new(:completely_bogus)
26
30
  lambda { r.adapter }.should raise_error(ArgumentError)
@@ -16,7 +16,11 @@ if ADAPTER
16
16
  repository(ADAPTER) { @zoo.save }
17
17
  end
18
18
 
19
- # --- Move somewhere ----
19
+ it 'should be serializable with Marshal' do
20
+ Marshal.load(Marshal.dump(@zoo)).should == @zoo
21
+ end
22
+
23
+ # --- Move somewhere ----
20
24
  it "should be able to destroy objects" do
21
25
  lambda { @zoo.destroy.should be_true }.should_not raise_error
22
26
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sam-dm-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.8
4
+ version: 0.9.9
5
5
  platform: ruby
6
6
  authors:
7
- - Sam Smoot
7
+ - Dan Kubb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-07-24 00:00:00 -07:00
12
+ date: 2009-01-04 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -19,7 +19,7 @@ dependencies:
19
19
  requirements:
20
20
  - - ~>
21
21
  - !ruby/object:Gem::Version
22
- version: 0.9.9
22
+ version: 0.9.10
23
23
  version:
24
24
  - !ruby/object:Gem::Dependency
25
25
  name: extlib
@@ -41,7 +41,7 @@ dependencies:
41
41
  version:
42
42
  description: Faster, Better, Simpler!
43
43
  email:
44
- - ssmoot@gmail.com
44
+ - dan.kubb@gmail.com
45
45
  executables: []
46
46
 
47
47
  extensions: []