composite_primary_keys 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,7 +34,10 @@ unless defined?(ActiveRecord)
34
34
  end
35
35
  end
36
36
 
37
+ require 'composite_primary_keys/fixtures'
37
38
  require 'composite_primary_keys/composite_arrays'
39
+ require 'composite_primary_keys/associations'
40
+ require 'composite_primary_keys/reflection'
38
41
  require 'composite_primary_keys/base'
39
42
 
40
43
  ActiveRecord::Base.class_eval do
@@ -0,0 +1,4 @@
1
+ module ActiveRecord
2
+ class Base
3
+ end
4
+ end
@@ -1,5 +1,8 @@
1
1
  module CompositePrimaryKeys
2
2
  module ActiveRecord #:nodoc:
3
+ class CompositeKeyError < StandardError #:nodoc:
4
+ end
5
+
3
6
  module Base #:nodoc:
4
7
 
5
8
  INVALID_FOR_COMPOSITE_KEYS = 'Not appropriate for composite primary keys'
@@ -51,7 +54,7 @@ module CompositePrimaryKeys
51
54
  end
52
55
 
53
56
  def id_before_type_cast #:nodoc:
54
- raise CompositePrimaryKeys::ActiveRecord::Base::NOT_IMPLEMENTED_YET
57
+ raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::NOT_IMPLEMENTED_YET
55
58
  end
56
59
 
57
60
  def quoted_id #:nodoc:
@@ -62,12 +65,14 @@ module CompositePrimaryKeys
62
65
  end
63
66
 
64
67
  # Sets the primary ID.
65
- def id=(value)
66
- ids = id.split(value) if value.is_a?(String)
68
+ def id=(ids)
69
+ ids = ids.split(ID_SEP) if ids.is_a?(String)
70
+ ids.flatten!
67
71
  unless ids.is_a?(Array) and ids.length == self.class.primary_keys.length
68
72
  raise "#{self.class}.id= requires #{self.class.primary_keys.length} ids"
69
73
  end
70
- ids.each {|id| write_attribute(self.class.primary_key , id)}
74
+ [primary_keys, ids].transpose.each {|key, an_id| write_attribute(key , an_id)}
75
+ id
71
76
  end
72
77
 
73
78
  # Deletes the record in the database and freezes this instance to reflect that no changes should
@@ -96,6 +101,36 @@ module CompositePrimaryKeys
96
101
  end
97
102
  end
98
103
 
104
+ # Allows +attr_name+ to be the list of primary_keys, and returns the id
105
+ # of the object
106
+ # e.g. @object[@object.class.primary_key] => [1,1]
107
+ def [](attr_name)
108
+ if attr_name.is_a?(String) and attr_name != attr_name.split(ID_SEP).first
109
+ attr_name = attr_name.split(ID_SEP)
110
+ end
111
+ attr_name.is_a?(Array) ?
112
+ attr_name.map {|name| read_attribute(name)} :
113
+ read_attribute(attr_name)
114
+ end
115
+
116
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
117
+ # (Alias for the protected write_attribute method).
118
+ def []=(attr_name, value)
119
+ if attr_name.is_a?(String) and attr_name != attr_name.split(ID_SEP).first
120
+ attr_name = attr_name.split(ID_SEP)
121
+ end
122
+ if attr_name.is_a? Array
123
+ value = value.split(ID_SEP) if value.is_a? String
124
+ unless value.length == attr_name.length
125
+ raise "Number of attr_names and values do not match"
126
+ end
127
+ #breakpoint
128
+ [attr_name, value].transpose.map {|name,val| write_attribute(name.to_s, val)}
129
+ else
130
+ write_attribute(attr_name, value)
131
+ end
132
+ end
133
+
99
134
  # Define an attribute reader method. Cope with nil column.
100
135
  def define_read_method(symbol, attr_name, column)
101
136
  cast_code = column.type_cast_code('v') if column
@@ -146,6 +181,25 @@ module CompositePrimaryKeys
146
181
  )
147
182
  return true
148
183
  end
184
+
185
+ # Creates a new record with values matching those of the instance attributes.
186
+ def create
187
+ if self.id.nil?
188
+ raise CompositeKeyError, "Composite keys do not generated ids from sequences, you must provide id values"
189
+ end
190
+
191
+ self.id = connection.insert(
192
+ "INSERT INTO #{self.class.table_name} " +
193
+ "(#{quoted_column_names.join(', ')}) " +
194
+ "VALUES(#{attributes_with_quotes.values.join(', ')})",
195
+ "#{self.class.name} Create",
196
+ self.class.primary_key, self.id
197
+ )
198
+
199
+ @new_record = false
200
+
201
+ return true
202
+ end
149
203
  end
150
204
 
151
205
  module CompositeClassMethods
@@ -207,40 +261,44 @@ module CompositePrimaryKeys
207
261
  # Lazy-set the sequence name to the connection's default. This method
208
262
  # is only ever called once since set_sequence_name overrides it.
209
263
  def sequence_name #:nodoc:
210
- raise CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
264
+ raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
211
265
  end
212
266
 
213
267
  def reset_sequence_name #:nodoc:
214
- raise CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
268
+ raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
215
269
  end
216
270
 
217
271
  def set_primary_key(value = nil, &block)
218
- raise CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
272
+ raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
219
273
  end
220
274
 
221
275
  private
222
276
  def find_one(id, options)
223
- raise CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
277
+ raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
224
278
  end
225
279
 
226
280
  def find_some(ids, options)
227
- raise CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
281
+ raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
228
282
  end
229
283
 
230
284
  def find_from_ids(ids, options)
231
285
  conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
232
286
  # if ids is just a flat list, then its size must = primary_key.length (one id per primary key, in order)
233
287
  # if ids is list of lists, then each inner list must follow rule above
234
- #if ids.first.is_a?(String) - find '2,1' -> find_from_ids ['2,1']
235
- ids = ids[0].split(';').map {|id_set| id_set.split ','} if ids.first.is_a? String
236
- ids = [ids] if not ids.first.kind_of?(Array)
237
288
 
289
+ if ids.first.is_a? String
290
+ # find '2,1' -> ids = ['2,1']
291
+ # find '2,1;7,3' -> ids = ['2,1;7,3']
292
+ ids = ids.first.split(ID_SET_SEP).map {|id_set| id_set.split(ID_SEP).to_composite_ids}
293
+ # find '2,1;7,3' -> ids = [['2','1'],['7','3']], inner [] are CompositeIds
294
+ end
295
+ ids = [ids.to_composite_ids] if not ids.first.kind_of?(Array)
238
296
  ids.each do |id_set|
239
297
  unless id_set.is_a?(Array)
240
298
  raise "Ids must be in an Array, instead received: #{id_set.inspect}"
241
299
  end
242
300
  unless id_set.length == primary_keys.length
243
- raise "Incorrect number of primary keys for #{class_name}: #{primary_keys.inspect}"
301
+ raise "#{id_set.inspect}: Incorrect number of primary keys for #{class_name}: #{primary_keys.inspect}"
244
302
  end
245
303
  end
246
304
 
@@ -1,5 +1,6 @@
1
1
  module CompositePrimaryKeys
2
2
  ID_SEP = ','
3
+ ID_SET_SEP = ';'
3
4
  module ArrayExtension
4
5
  def to_composite_keys
5
6
  CompositeKeys.new(self)
@@ -1,600 +1,9 @@
1
- require 'erb'
2
- require 'yaml'
3
- require 'csv'
4
-
5
- module YAML #:nodoc:
6
- class Omap #:nodoc:
7
- def keys; map { |k, v| k } end
8
- def values; map { |k, v| v } end
9
- end
10
- end
11
-
12
- class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
13
- end
14
-
15
- # Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavours:
16
- #
17
- # 1. YAML fixtures
18
- # 2. CSV fixtures
19
- # 3. Single-file fixtures
20
- #
21
- # = YAML fixtures
22
- #
23
- # This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
24
- # in a non-verbose, humanly-readable format. It ships with Ruby 1.8.1+.
25
- #
26
- # Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed
27
- # by <tt>Test::Unit::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
28
- # put your files in <your-rails-app>/test/fixtures/). The fixture file ends with the .yml file extension (Rails example:
29
- # "<your-rails-app>/test/fixtures/web_sites.yml"). The format of a YAML fixture file looks like this:
30
- #
31
- # rubyonrails:
32
- # id: 1
33
- # name: Ruby on Rails
34
- # url: http://www.rubyonrails.org
35
- #
36
- # google:
37
- # id: 2
38
- # name: Google
39
- # url: http://www.google.com
40
- #
41
- # This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
42
- # indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
43
- # pleasure.
44
- #
45
- # Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type. See http://yaml.org/type/omap.html
46
- # for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
47
- # This is commonly needed for tree structures. Example:
48
- #
49
- # --- !omap
50
- # - parent:
51
- # id: 1
52
- # parent_id: NULL
53
- # title: Parent
54
- # - child:
55
- # id: 2
56
- # parent_id: 1
57
- # title: Child
58
- #
59
- # = CSV fixtures
60
- #
61
- # Fixtures can also be kept in the Comma Separated Value format. Akin to YAML fixtures, CSV fixtures are stored
62
- # in a single file, but instead end with the .csv file extension (Rails example: "<your-rails-app>/test/fixtures/web_sites.csv")
63
- #
64
- # The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
65
- # humans. The first line of the CSV file is a comma-separated list of field names. The rest of the file is then comprised
66
- # of the actual data (1 per line). Here's an example:
67
- #
68
- # id, name, url
69
- # 1, Ruby On Rails, http://www.rubyonrails.org
70
- # 2, Google, http://www.google.com
71
- #
72
- # Should you have a piece of data with a comma character in it, you can place double quotes around that value. If you
73
- # need to use a double quote character, you must escape it with another double quote.
74
- #
75
- # Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats. Instead, the
76
- # fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
77
- # number to the end. In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
78
- # "web_site_2".
79
- #
80
- # Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
81
- # have existing data somewhere already.
82
- #
83
- # = Single-file fixtures
84
- #
85
- # This type of fixtures was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
86
- # Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
87
- # appointed by <tt>Test::Unit::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
88
- # put your files in <your-rails-app>/test/fixtures/<your-model-name>/ -- like <your-rails-app>/test/fixtures/web_sites/ for the WebSite
89
- # model).
90
- #
91
- # Each text file placed in this directory represents a "record". Usually these types of fixtures are named without
92
- # extensions, but if you are on a Windows machine, you might consider adding .txt as the extension. Here's what the
93
- # above example might look like:
94
- #
95
- # web_sites/google
96
- # web_sites/yahoo.txt
97
- # web_sites/ruby-on-rails
98
- #
99
- # The file format of a standard fixture is simple. Each line is a property (or column in db speak) and has the syntax
100
- # of "name => value". Here's an example of the ruby-on-rails fixture above:
101
- #
102
- # id => 1
103
- # name => Ruby on Rails
104
- # url => http://www.rubyonrails.org
105
- #
106
- # = Using Fixtures
107
- #
108
- # Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
109
- # fixtures, but first let's take a look at a sample unit test found:
110
- #
111
- # require 'web_site'
112
- #
113
- # class WebSiteTest < Test::Unit::TestCase
114
- # def test_web_site_count
115
- # assert_equal 2, WebSite.count
116
- # end
117
- # end
118
- #
119
- # As it stands, unless we pre-load the web_site table in our database with two records, this test will fail. Here's the
120
- # easiest way to add fixtures to the database:
121
- #
122
- # ...
123
- # class WebSiteTest < Test::Unit::TestCase
124
- # fixtures :web_sites # add more by separating the symbols with commas
125
- # ...
126
- #
127
- # By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here tho), we trigger
128
- # the testing environment to automatically load the appropriate fixtures into the database before each test.
129
- # To ensure consistent data, the environment deletes the fixtures before running the load.
130
- #
131
- # In addition to being available in the database, the fixtures are also loaded into a hash stored in an instance variable
132
- # of the test case. It is named after the symbol... so, in our example, there would be a hash available called
133
- # @web_sites. This is where the "fixture name" comes into play.
134
- #
135
- # On top of that, each record is automatically "found" (using Model.find(id)) and placed in the instance variable of its name.
136
- # So for the YAML fixtures, we'd get @rubyonrails and @google, which could be interrogated using regular Active Record semantics:
137
- #
138
- # # test if the object created from the fixture data has the same attributes as the data itself
139
- # def test_find
140
- # assert_equal @web_sites["rubyonrails"]["name"], @rubyonrails.name
141
- # end
142
- #
143
- # As seen above, the data hash created from the YAML fixtures would have @web_sites["rubyonrails"]["url"] return
144
- # "http://www.rubyonrails.org" and @web_sites["google"]["name"] would return "Google". The same fixtures, but loaded
145
- # from a CSV fixture file, would be accessible via @web_sites["web_site_1"]["name"] == "Ruby on Rails" and have the individual
146
- # fixtures available as instance variables @web_site_1 and @web_site_2.
147
- #
148
- # If you do not wish to use instantiated fixtures (usually for performance reasons) there are two options.
149
- #
150
- # - to completely disable instantiated fixtures:
151
- # self.use_instantiated_fixtures = false
152
- #
153
- # - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance:
154
- # self.use_instantiated_fixtures = :no_instances
155
- #
156
- # Even if auto-instantiated fixtures are disabled, you can still access them
157
- # by name via special dynamic methods. Each method has the same name as the
158
- # model, and accepts the name of the fixture to instantiate:
159
- #
160
- # fixtures :web_sites
161
- #
162
- # def test_find
163
- # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
164
- # end
165
- #
166
- # = Dynamic fixtures with ERb
167
- #
168
- # Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
169
- # mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
170
- #
171
- # <% for i in 1..1000 %>
172
- # fix_<%= i %>:
173
- # id: <%= i %>
174
- # name: guy_<%= 1 %>
175
- # <% end %>
176
- #
177
- # This will create 1000 very simple YAML fixtures.
178
- #
179
- # Using ERb, you can also inject dynamic values into your fixtures with inserts like <%= Date.today.strftime("%Y-%m-%d") %>.
180
- # This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
181
- # sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application
182
- # is properly testable. Hence, dynamic values in fixtures are to be considered a code smell.
183
- #
184
- # = Transactional fixtures
185
- #
186
- # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
187
- # They can also turn off auto-instantiation of fixture data since the feature is costly and often unused.
188
- #
189
- # class FooTest < Test::Unit::TestCase
190
- # self.use_transactional_fixtures = true
191
- # self.use_instantiated_fixtures = false
192
- #
193
- # fixtures :foos
194
- #
195
- # def test_godzilla
196
- # assert !Foo.find(:all).empty?
197
- # Foo.destroy_all
198
- # assert Foo.find(:all).empty?
199
- # end
200
- #
201
- # def test_godzilla_aftermath
202
- # assert !Foo.find(:all).empty?
203
- # end
204
- # end
205
- #
206
- # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
207
- # then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes.
208
- #
209
- # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
210
- # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
211
- #
212
- # When *not* to use transactional fixtures:
213
- # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
214
- # particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
215
- # the results of your transaction until Active Record supports nested transactions or savepoints (in progress.)
216
- # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
217
- # Use InnoDB, MaxDB, or NDB instead.
218
- class Fixtures < YAML::Omap
219
- DEFAULT_FILTER_RE = /\.ya?ml$/
220
-
221
- def self.instantiate_fixtures(object, table_name, fixtures, load_instances=true)
222
- object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures
223
- if load_instances
224
- ActiveRecord::Base.silence do
225
- fixtures.each do |name, fixture|
226
- begin
227
- object.instance_variable_set "@#{name}", fixture.find
228
- rescue FixtureClassNotFound
229
- nil
230
- end
231
- end
232
- end
233
- end
234
- end
235
-
236
- def self.instantiate_all_loaded_fixtures(object, load_instances=true)
237
- all_loaded_fixtures.each do |table_name, fixtures|
238
- Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
239
- end
240
- end
241
-
242
- cattr_accessor :all_loaded_fixtures
243
- self.all_loaded_fixtures = {}
244
-
245
- def self.create_fixtures(fixtures_directory, table_names, class_names = {})
246
- table_names = [table_names].flatten.map { |n| n.to_s }
247
- connection = block_given? ? yield : ActiveRecord::Base.connection
248
- ActiveRecord::Base.silence do
249
- fixtures_map = {}
250
- fixtures = table_names.map do |table_name|
251
- fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s))
252
- end
253
- all_loaded_fixtures.merge! fixtures_map
254
-
255
- connection.transaction do
256
- fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
257
- fixtures.each { |fixture| fixture.insert_fixtures }
258
-
259
- # Cap primary key sequences to max(pk).
260
- if connection.respond_to?(:reset_pk_sequence!)
261
- table_names.each do |table_name|
262
- connection.reset_pk_sequence!(table_name)
263
- end
264
- end
265
- end
266
-
267
- return fixtures.size > 1 ? fixtures : fixtures.first
268
- end
269
- end
270
-
271
-
272
- attr_reader :table_name
273
-
274
- def initialize(connection, table_name, class_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
275
- @connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
276
- @class_name = class_name ||
277
- (ActiveRecord::Base.pluralize_table_names ? @table_name.singularize.camelize : @table_name.camelize)
278
- @table_name = ActiveRecord::Base.table_name_prefix + @table_name + ActiveRecord::Base.table_name_suffix
279
- read_fixture_files
280
- end
281
-
282
- def delete_existing_fixtures
283
- @connection.delete "DELETE FROM #{@table_name}", 'Fixture Delete'
284
- end
285
-
286
- def insert_fixtures
287
- values.each do |fixture|
288
- @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
289
- end
290
- end
291
-
292
- private
293
-
294
- def read_fixture_files
295
- if File.file?(yaml_file_path)
296
- # YAML fixtures
297
- begin
298
- yaml_string = ""
299
- Dir["#{@fixture_path}/**/*.yml"].select {|f| test(?f,f) }.each do |subfixture_path|
300
- yaml_string << IO.read(subfixture_path)
301
- end
302
- yaml_string << IO.read(yaml_file_path)
303
-
304
- if yaml = YAML::load(erb_render(yaml_string))
305
- yaml = yaml.value if yaml.respond_to?(:type_id) and yaml.respond_to?(:value)
306
- yaml.each do |name, data|
307
- self[name] = Fixture.new(data, @class_name)
308
- end
309
- end
310
- rescue Exception=>boom
311
- raise Fixture::FormatError, "a YAML error occured parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{boom.class}: #{boom}"
312
- end
313
- elsif File.file?(csv_file_path)
314
- # CSV fixtures
315
- reader = CSV::Reader.create(erb_render(IO.read(csv_file_path)))
316
- header = reader.shift
317
- i = 0
318
- reader.each do |row|
319
- data = {}
320
- row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
321
- self["#{Inflector::underscore(@class_name)}_#{i+=1}"]= Fixture.new(data, @class_name)
322
- end
323
- elsif File.file?(deprecated_yaml_file_path)
324
- raise Fixture::FormatError, ".yml extension required: rename #{deprecated_yaml_file_path} to #{yaml_file_path}"
325
- else
326
- # Standard fixtures
327
- Dir.entries(@fixture_path).each do |file|
328
- path = File.join(@fixture_path, file)
329
- if File.file?(path) and file !~ @file_filter
330
- self[file] = Fixture.new(path, @class_name)
331
- end
332
- end
333
- end
334
- end
335
-
336
- def yaml_file_path
337
- "#{@fixture_path}.yml"
338
- end
339
-
340
- def deprecated_yaml_file_path
341
- "#{@fixture_path}.yaml"
342
- end
343
-
344
- def csv_file_path
345
- @fixture_path + ".csv"
346
- end
347
-
348
- def yaml_fixtures_key(path)
349
- File.basename(@fixture_path).split(".").first
350
- end
351
-
352
- def erb_render(fixture_content)
353
- ERB.new(fixture_content).result
354
- end
355
- end
356
-
357
1
  class Fixture #:nodoc:
358
- include Enumerable
359
- class FixtureError < StandardError#:nodoc:
360
- end
361
- class FormatError < FixtureError#:nodoc:
362
- end
363
-
364
- def initialize(fixture, class_name)
365
- case fixture
366
- when Hash, YAML::Omap
367
- @fixture = fixture
368
- when String
369
- @fixture = read_fixture_file(fixture)
370
- else
371
- raise ArgumentError, "Bad fixture argument #{fixture.inspect}"
372
- end
373
-
374
- @class_name = class_name
375
- end
376
-
377
- def each
378
- @fixture.each { |item| yield item }
379
- end
380
-
381
2
  def [](key)
382
- @fixture[key]
383
- end
384
-
385
- def to_hash
386
- @fixture
387
- end
388
-
389
- def key_list
390
- columns = @fixture.keys.collect{ |column_name| ActiveRecord::Base.connection.quote_column_name(column_name) }
391
- columns.join(", ")
392
- end
393
-
394
- def value_list
395
- @fixture.values.map { |v| ActiveRecord::Base.connection.quote(v).gsub('\\n', "\n").gsub('\\r', "\r") }.join(", ")
396
- end
397
-
398
- def find
399
- klass = @class_name.is_a?(Class) ? @class_name : Object.const_get(@class_name) rescue nil
400
- if klass
401
- klass.find(self[klass.primary_key])
402
- else
403
- raise FixtureClassNotFound, "The class #{@class_name.inspect} was not found."
3
+ if key.is_a? Array
4
+ return key.map {|a_key| self[a_key.to_s]}.
5
+ to_composite_ids.to_s
404
6
  end
7
+ @fixture[key]
405
8
  end
406
-
407
- private
408
- def read_fixture_file(fixture_file_path)
409
- IO.readlines(fixture_file_path).inject({}) do |fixture, line|
410
- # Mercifully skip empty lines.
411
- next if line =~ /^\s*$/
412
-
413
- # Use the same regular expression for attributes as Active Record.
414
- unless md = /^\s*([a-zA-Z][-_\w]*)\s*=>\s*(.+)\s*$/.match(line)
415
- raise FormatError, "#{fixture_file_path}: fixture format error at '#{line}'. Expecting 'key => value'."
416
- end
417
- key, value = md.captures
418
-
419
- # Disallow duplicate keys to catch typos.
420
- raise FormatError, "#{fixture_file_path}: duplicate '#{key}' in fixture." if fixture[key]
421
- fixture[key] = value.strip
422
- fixture
423
- end
424
- end
425
- end
426
-
427
- module Test #:nodoc:
428
- module Unit #:nodoc:
429
- class TestCase #:nodoc:
430
- cattr_accessor :fixture_path
431
- class_inheritable_accessor :fixture_table_names
432
- class_inheritable_accessor :fixture_class_names
433
- class_inheritable_accessor :use_transactional_fixtures
434
- class_inheritable_accessor :use_instantiated_fixtures # true, false, or :no_instances
435
- class_inheritable_accessor :pre_loaded_fixtures
436
-
437
- self.fixture_table_names = []
438
- self.use_transactional_fixtures = false
439
- self.use_instantiated_fixtures = true
440
- self.pre_loaded_fixtures = false
441
-
442
- self.fixture_class_names = {}
443
-
444
- @@already_loaded_fixtures = {}
445
- self.fixture_class_names = {}
446
-
447
- def self.set_fixture_class(class_names = {})
448
- self.fixture_class_names = self.fixture_class_names.merge(class_names)
449
- end
450
-
451
- def self.fixtures(*table_names)
452
- table_names = table_names.flatten.map { |n| n.to_s }
453
- self.fixture_table_names |= table_names
454
- require_fixture_classes(table_names)
455
- setup_fixture_accessors(table_names)
456
- end
457
-
458
- def self.require_fixture_classes(table_names=nil)
459
- (table_names || fixture_table_names).each do |table_name|
460
- file_name = table_name.to_s
461
- file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
462
- begin
463
- require file_name
464
- rescue LoadError
465
- # Let's hope the developer has included it himself
466
- end
467
- end
468
- end
469
-
470
- def self.setup_fixture_accessors(table_names=nil)
471
- (table_names || fixture_table_names).each do |table_name|
472
- table_name = table_name.to_s.tr('.','_')
473
- define_method(table_name) do |fixture, *optionals|
474
- force_reload = optionals.shift
475
- @fixture_cache[table_name] ||= Hash.new
476
- @fixture_cache[table_name][fixture] = nil if force_reload
477
- if @loaded_fixtures[table_name][fixture.to_s]
478
- @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find
479
- else
480
- raise StandardError, "No fixture with name '#{fixture}' found for table '#{table_name}'"
481
- end
482
- end
483
- end
484
- end
485
-
486
- def self.uses_transaction(*methods)
487
- @uses_transaction ||= []
488
- @uses_transaction.concat methods.map { |m| m.to_s }
489
- end
490
-
491
- def self.uses_transaction?(method)
492
- @uses_transaction && @uses_transaction.include?(method.to_s)
493
- end
494
-
495
- def use_transactional_fixtures?
496
- use_transactional_fixtures &&
497
- !self.class.uses_transaction?(method_name)
498
- end
499
-
500
- def setup_with_fixtures
501
- if pre_loaded_fixtures && !use_transactional_fixtures
502
- raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
503
- end
504
-
505
- @fixture_cache = Hash.new
506
-
507
- # Load fixtures once and begin transaction.
508
- if use_transactional_fixtures?
509
- if @@already_loaded_fixtures[self.class]
510
- @loaded_fixtures = @@already_loaded_fixtures[self.class]
511
- else
512
- load_fixtures
513
- @@already_loaded_fixtures[self.class] = @loaded_fixtures
514
- end
515
- ActiveRecord::Base.lock_mutex
516
- ActiveRecord::Base.connection.begin_db_transaction
517
-
518
- # Load fixtures for every test.
519
- else
520
- @@already_loaded_fixtures[self.class] = nil
521
- load_fixtures
522
- end
523
-
524
- # Instantiate fixtures for every test if requested.
525
- instantiate_fixtures if use_instantiated_fixtures
526
- end
527
-
528
- alias_method :setup, :setup_with_fixtures
529
-
530
- def teardown_with_fixtures
531
- # Rollback changes.
532
- if use_transactional_fixtures?
533
- ActiveRecord::Base.connection.rollback_db_transaction
534
- ActiveRecord::Base.unlock_mutex
535
- end
536
- ActiveRecord::Base.verify_active_connections!
537
- end
538
-
539
- alias_method :teardown, :teardown_with_fixtures
540
-
541
- def self.method_added(method)
542
- case method.to_s
543
- when 'setup'
544
- unless method_defined?(:setup_without_fixtures)
545
- alias_method :setup_without_fixtures, :setup
546
- define_method(:setup) do
547
- setup_with_fixtures
548
- setup_without_fixtures
549
- end
550
- end
551
- when 'teardown'
552
- unless method_defined?(:teardown_without_fixtures)
553
- alias_method :teardown_without_fixtures, :teardown
554
- define_method(:teardown) do
555
- teardown_without_fixtures
556
- teardown_with_fixtures
557
- end
558
- end
559
- end
560
- end
561
-
562
- private
563
- def load_fixtures
564
- @loaded_fixtures = {}
565
- fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
566
- unless fixtures.nil?
567
- if fixtures.instance_of?(Fixtures)
568
- @loaded_fixtures[fixtures.table_name] = fixtures
569
- else
570
- fixtures.each { |f| @loaded_fixtures[f.table_name] = f }
571
- end
572
- end
573
- end
574
-
575
- # for pre_loaded_fixtures, only require the classes once. huge speed improvement
576
- @@required_fixture_classes = false
577
-
578
- def instantiate_fixtures
579
- if pre_loaded_fixtures
580
- raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty?
581
- unless @@required_fixture_classes
582
- self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
583
- @@required_fixture_classes = true
584
- end
585
- Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
586
- else
587
- raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
588
- @loaded_fixtures.each do |table_name, fixtures|
589
- Fixtures.instantiate_fixtures(self, table_name, fixtures, load_instances?)
590
- end
591
- end
592
- end
593
-
594
- def load_instances?
595
- use_instantiated_fixtures != :no_instances
596
- end
597
- end
598
-
599
- end
600
- end
9
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveRecord
2
+ module Reflection
3
+ class AssociationReflection
4
+ def primary_key_name
5
+ return @primary_key_name if @primary_key_name
6
+ case
7
+ when macro == :belongs_to
8
+ @primary_key_name = options[:foreign_key] || class_name.foreign_key
9
+ when options[:as]
10
+ @primary_key_name = options[:foreign_key] || "#{options[:as]}_id"
11
+ else
12
+ @primary_key_name = options[:foreign_key] || active_record.name.foreign_key
13
+ end
14
+ @primary_key_name = @primary_key_name.to_composite_keys.to_s if @primary_key_name.is_a? Array
15
+ @primary_key_name
16
+ end
17
+ end
18
+ end
19
+ end
@@ -2,7 +2,7 @@ module CompositePrimaryKeys
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
- TINY = 2
5
+ TINY = 3
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -8,7 +8,6 @@ require 'active_support/binding_of_caller'
8
8
  require 'active_support/breakpoint'
9
9
  require 'connection'
10
10
  require 'composite_primary_keys'
11
- require 'composite_primary_keys/fixtures'
12
11
 
13
12
  QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type') unless Object.const_defined?(:QUOTED_TYPE)
14
13
 
@@ -63,11 +62,12 @@ protected
63
62
  end
64
63
 
65
64
  def first_id
66
- (1..@primary_keys.length).map {|num| 1}
65
+ ids = (1..@primary_keys.length).map {|num| 1}
66
+ composite? ? ids.to_composite_ids : ids.first
67
67
  end
68
68
 
69
69
  def first_id_str
70
- first_id.join(CompositePrimaryKeys::ID_SEP)
70
+ composite? ? first_id.join(CompositePrimaryKeys::ID_SEP) : first_id.to_s
71
71
  end
72
72
 
73
73
  def composite?
@@ -0,0 +1,44 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/product'
3
+ require 'fixtures/tariff'
4
+ require 'fixtures/product_tariff'
5
+
6
+ class AssociationTest < Test::Unit::TestCase
7
+ fixtures :products, :tariffs, :product_tariffs
8
+
9
+ def setup
10
+ super
11
+ @first_product = products(:first_product)
12
+ @flat = tariffs(:flat)
13
+ @free = tariffs(:free)
14
+ @first_flat = product_tariffs(:first_flat)
15
+ end
16
+
17
+ def test_setup
18
+ assert_not_nil @first_product
19
+ assert_not_nil @flat
20
+ assert_not_nil @free
21
+ assert_not_nil @first_flat
22
+ end
23
+
24
+ def test_products
25
+ assert_not_nil @first_product.product_tariffs
26
+ assert_equal 2, @first_product.product_tariffs.length
27
+ assert_not_nil @first_product.tariffs
28
+ assert_equal 2, @first_product.tariffs.length
29
+ end
30
+
31
+ def test_product_tariffs
32
+ assert_not_nil @first_flat.product
33
+ assert_not_nil @first_flat.tariff
34
+ assert_equal Product, @first_flat.product.class
35
+ assert_equal Tariff, @first_flat.tariff.class
36
+ end
37
+
38
+ def test_tariffs
39
+ assert_not_nil @flat.product_tariffs
40
+ assert_equal 2, @flat.product_tariffs.length
41
+ assert_not_nil @flat.products
42
+ assert_equal 2, @flat.products.length
43
+ end
44
+ end
@@ -0,0 +1,85 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/reference_type'
3
+ require 'fixtures/reference_code'
4
+ require 'fixtures/product'
5
+ require 'fixtures/tariff'
6
+ require 'fixtures/product_tariff'
7
+
8
+ class AttributesTest < Test::Unit::TestCase
9
+ fixtures :reference_types, :reference_codes, :products, :tariffs, :product_tariffs
10
+
11
+ CLASSES = {
12
+ :single => {
13
+ :class => ReferenceType,
14
+ :primary_keys => [:reference_type_id],
15
+ },
16
+ :dual => {
17
+ :class => ReferenceCode,
18
+ :primary_keys => [:reference_type_id, :reference_code],
19
+ },
20
+ }
21
+
22
+ def setup
23
+ super
24
+ self.class.classes = CLASSES
25
+ end
26
+
27
+ def test_brackets
28
+ testing_with do
29
+ @first.attributes.each_pair do |attr_name, value|
30
+ assert_equal value, @first[attr_name]
31
+ end
32
+ end
33
+ end
34
+
35
+ def test_brackets_primary_key
36
+ testing_with do
37
+ assert_equal @first.id, @first[@primary_keys]
38
+ assert_equal @first.id, @first[@first.class.primary_key]
39
+ end
40
+ end
41
+
42
+ def test_brackets_assignment
43
+ testing_with do
44
+ @first.attributes.each_pair do |attr_name, value|
45
+ @first[attr_name]= !value.nil? ? value * 2 : '1'
46
+ assert_equal !value.nil? ? value * 2 : '1', @first[attr_name]
47
+ end
48
+ end
49
+ end
50
+
51
+ def test_brackets_foreign_key_assignment
52
+ @flat = tariffs(:flat)
53
+ @second_free = product_tariffs(:second_free)
54
+ @second_free_fk = [:tariff_id, :tariff_start_date]
55
+ @second_free[key = @second_free_fk] = @flat.id
56
+ compare_indexes('@flat', @flat.class.primary_key, '@second_free', @second_free_fk)
57
+ assert_equal @flat.id, @second_free[key]
58
+ @second_free[key = @second_free_fk.to_composite_ids] = @flat.id
59
+ assert_equal @flat.id, @second_free[key]
60
+ compare_indexes('@flat', @flat.class.primary_key, '@second_free', @second_free_fk)
61
+ @second_free[key = @second_free_fk.to_composite_ids] = @flat.id.to_s
62
+ assert_equal @flat.id, @second_free[key]
63
+ compare_indexes('@flat', @flat.class.primary_key, '@second_free', @second_free_fk)
64
+ @second_free[key = @second_free_fk.to_composite_ids] = @flat.id.to_s
65
+ assert_equal @flat.id, @second_free[key]
66
+ compare_indexes('@flat', @flat.class.primary_key, '@second_free', @second_free_fk)
67
+ @second_free[key = @second_free_fk.to_composite_ids.to_s] = @flat.id
68
+ assert_equal @flat.id, @second_free[key]
69
+ compare_indexes('@flat', @flat.class.primary_key, '@second_free', @second_free_fk)
70
+ @second_free[key = @second_free_fk.to_composite_ids.to_s] = @flat.id.to_s
71
+ assert_equal @flat.id, @second_free[key]
72
+ compare_indexes('@flat', @flat.class.primary_key, '@second_free', @second_free_fk)
73
+ end
74
+ private
75
+ def compare_indexes(obj_name1, indexes1, obj_name2, indexes2)
76
+ obj1, obj2 = eval "[#{obj_name1}, #{obj_name2}]"
77
+ indexes1.length.times do |key_index|
78
+ assert_equal obj1[indexes1[key_index].to_s],
79
+ obj2[indexes2[key_index].to_s],
80
+ "#{obj_name1}[#{indexes1[key_index]}]=#{obj1[indexes1[key_index].to_s].inspect} != " +
81
+ "#{obj_name2}[#{indexes2[key_index]}]=#{obj2[indexes2[key_index].to_s].inspect}; " +
82
+ "#{obj_name2} = #{obj2.inspect}"
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,52 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/reference_type'
3
+ require 'fixtures/reference_code'
4
+
5
+ class DummyTest < Test::Unit::TestCase
6
+ fixtures :reference_types, :reference_codes
7
+
8
+ CLASSES = {
9
+ :single => {
10
+ :class => ReferenceType,
11
+ :primary_keys => [:reference_type_id],
12
+ :create => {:reference_type_id => 10, :type_label => 'NEW_TYPE', :abbreviation => 'New Type'}
13
+ },
14
+ :dual => {
15
+ :class => ReferenceCode,
16
+ :primary_keys => [:reference_type_id, :reference_code],
17
+ :create => {:reference_type_id => 1, :reference_code => 20, :code_label => 'NEW_CODE', :abbreviation => 'New Code'}
18
+ },
19
+ }
20
+
21
+ def setup
22
+ super
23
+ self.class.classes = CLASSES
24
+ end
25
+
26
+ def test_setup
27
+ testing_with do
28
+ assert_not_nil @klass_info[:create]
29
+ end
30
+ end
31
+
32
+ def test_create
33
+ testing_with do
34
+ assert new_obj = @klass.create(@klass_info[:create])
35
+ assert !new_obj.new_record?
36
+ end
37
+ end
38
+
39
+ def test_create_no_id
40
+ testing_with do
41
+ begin
42
+ @obj = @klass.create(@klass_info[:create].block(@klass.primary_key))
43
+ @successful = !composite?
44
+ rescue CompositePrimaryKeys::ActiveRecord::CompositeKeyError
45
+ @successful = false
46
+ rescue
47
+ flunk "Incorrect exception raised: #{$!}, #{$!.class}"
48
+ end
49
+ assert_equal composite?, !@successful, "Create should have failed for composites; #{@obj.inspect}"
50
+ end
51
+ end
52
+ end
@@ -17,6 +17,7 @@ class DummyTest < Test::Unit::TestCase
17
17
  }
18
18
 
19
19
  def setup
20
+ super
20
21
  self.class.classes = CLASSES
21
22
  end
22
23
 
@@ -40,6 +40,16 @@ class FindTest < Test::Unit::TestCase
40
40
  end
41
41
  end
42
42
 
43
+ def test_find_composite_ids
44
+ testing_with do
45
+ found = @klass.find(first_id) # e.g. find([1,1].to_composite_ids)
46
+ assert found
47
+ assert_equal @klass, found.class
48
+ assert_equal found, @klass.find(found.id)
49
+ assert_equal found, @klass.find(found.to_param)
50
+ end
51
+ end
52
+
43
53
  def test_to_param
44
54
  testing_with do
45
55
  assert_equal first_id_str, @first.to_param.to_s
@@ -1,30 +1,2 @@
1
- DROP TABLE accounts;
2
- DROP TABLE funny_jokes;
3
- DROP TABLE companies;
4
- DROP TABLE topics;
5
- DROP TABLE developers;
6
- DROP TABLE projects;
7
- DROP TABLE developers_projects;
8
- DROP TABLE customers;
9
- DROP TABLE orders;
10
- DROP TABLE movies;
11
- DROP TABLE subscribers;
12
- DROP TABLE booleantests;
13
- DROP TABLE auto_id_tests;
14
- DROP TABLE entrants;
15
- DROP TABLE colnametests;
16
- DROP TABLE mixins;
17
- DROP TABLE people;
18
- DROP TABLE readers;
19
- DROP TABLE binaries;
20
- DROP TABLE computers;
21
- DROP TABLE tasks;
22
- DROP TABLE posts;
23
- DROP TABLE comments;
24
- DROP TABLE authors;
25
- DROP TABLE categories;
26
- DROP TABLE categories_posts;
27
- DROP TABLE fk_test_has_fk;
28
- DROP TABLE fk_test_has_pk;
29
- DROP TABLE keyboards;
30
- DROP TABLE legacy_things;
1
+ DROP TABLE reference_codes;
2
+ DROP TABLE reference_types;
@@ -14,3 +14,24 @@ CREATE TABLE `reference_codes` (
14
14
  `description` varchar(50) default NULL,
15
15
  PRIMARY KEY (`reference_type_id`,`reference_code`)
16
16
  ) TYPE=InnoDB;
17
+
18
+ CREATE TABLE `products` (
19
+ `id` int(11) NOT NULL auto_increment,
20
+ `name` varchar(50) default NULL,
21
+ PRIMARY KEY (`id`)
22
+ ) TYPE=InnoDB;
23
+
24
+ CREATE TABLE `tariffs` (
25
+ `tariff_id` int(11) NOT NULL,
26
+ `start_date` date NOT NULL,
27
+ `amount` integer(11) default NULL,
28
+ PRIMARY KEY (`tariff_id`,`start_date`)
29
+ ) TYPE=InnoDB;
30
+
31
+ CREATE TABLE `product_tariffs` (
32
+ `product_id` int(11) NOT NULL,
33
+ `tariff_id` int(11) NOT NULL,
34
+ `tariff_start_date` date NOT NULL,
35
+ PRIMARY KEY (`product_id`,`tariff_id`,`tariff_start_date`)
36
+ ) TYPE=InnoDB;
37
+
@@ -0,0 +1,5 @@
1
+ class Product < ActiveRecord::Base
2
+ set_primary_key :id # redundant
3
+ has_many :product_tariffs, :foreign_key => :product_id
4
+ has_many :tariffs, :through => :product_tariffs, :foreign_key => :product_id
5
+ end
@@ -0,0 +1,5 @@
1
+ class ProductTariff < ActiveRecord::Base
2
+ set_primary_keys :product_id, :tariff_id, :tariff_start_date
3
+ belongs_to :product, :foreign_key => :product_id
4
+ belongs_to :tariff, :foreign_key => [:tariff_id, :tariff_start_date]
5
+ end
@@ -0,0 +1,12 @@
1
+ first_flat:
2
+ product_id: 1
3
+ tariff_id: 1
4
+ tariff_start_date: <%= Date.today.to_s(:db) %>
5
+ first_free:
6
+ product_id: 1
7
+ tariff_id: 2
8
+ tariff_start_date: <%= Date.today.to_s(:db) %>
9
+ second_free:
10
+ product_id: 2
11
+ tariff_id: 2
12
+ tariff_start_date: <%= Date.today.to_s(:db) %>
@@ -0,0 +1,6 @@
1
+ first_product:
2
+ id: 1
3
+ name: Product One
4
+ second_product:
5
+ id: 2
6
+ name: Product Two
@@ -0,0 +1,4 @@
1
+ class Tariff < ActiveRecord::Base
2
+ set_primary_keys [:tariff_id, :start_date]
3
+ has_many :product_tariffs, :foreign_key => [:tariff_id, :tariff_start_date]
4
+ end
@@ -0,0 +1,13 @@
1
+ flat:
2
+ tariff_id: 1
3
+ start_date: <%= Date.today.to_s(:db) %>
4
+ amount: 50
5
+ free:
6
+ tariff_id: 2
7
+ start_date: <%= Date.today.to_s(:db) %>
8
+ amount: 0
9
+ flat_future:
10
+ tariff_id: 1
11
+ start_date: <%= Date.today.next.to_s(:db) %>
12
+ amount: 100
13
+
@@ -42,6 +42,33 @@ class IdsTest < Test::Unit::TestCase
42
42
  end
43
43
  end
44
44
 
45
+ def test_set_ids_string
46
+ testing_with do
47
+ array = @primary_keys.collect {|key| 5}
48
+ expected = composite? ? array.to_composite_keys : array.first
49
+ @first.id = expected.to_s
50
+ assert_equal expected, @first.id
51
+ end
52
+ end
53
+
54
+ def test_set_ids_array
55
+ testing_with do
56
+ array = @primary_keys.collect {|key| 5}
57
+ expected = composite? ? array.to_composite_keys : array.first
58
+ @first.id = expected
59
+ assert_equal expected, @first.id
60
+ end
61
+ end
62
+
63
+ def test_set_ids_comp
64
+ testing_with do
65
+ array = @primary_keys.collect {|key| 5}
66
+ expected = composite? ? array.to_composite_keys : array.first
67
+ @first.id = expected
68
+ assert_equal expected, @first.id
69
+ end
70
+ end
71
+
45
72
  def test_primary_keys
46
73
  testing_with do
47
74
  if composite?
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: composite_primary_keys
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.2
7
- date: 2006-07-23 00:00:00 +02:00
6
+ version: 0.3.3
7
+ date: 2006-07-30 00:00:00 +02:00
8
8
  summary: Support for composite primary keys in ActiveRecords
9
9
  require_paths:
10
10
  - lib
@@ -34,10 +34,12 @@ files:
34
34
  - CHANGELOG
35
35
  - lib/composite_primary_keys
36
36
  - lib/composite_primary_keys.rb
37
- - lib/composite_primary_keys/fixtures.rb
38
37
  - lib/composite_primary_keys/composite_arrays.rb
39
38
  - lib/composite_primary_keys/version.rb
40
39
  - lib/composite_primary_keys/base.rb
40
+ - lib/composite_primary_keys/fixtures.rb
41
+ - lib/composite_primary_keys/reflection.rb
42
+ - lib/composite_primary_keys/associations.rb
41
43
  - test/connections
42
44
  - test/fixtures
43
45
  - test/composite_arrays_test.rb
@@ -51,6 +53,9 @@ files:
51
53
  - test/pagination_test.rb
52
54
  - test/dummy_test.rb
53
55
  - test/clone_test.rb
56
+ - test/associations_test.rb
57
+ - test/attributes_test.rb
58
+ - test/create_test.rb
54
59
  - test/connections/native_mysql
55
60
  - test/connections/native_mysql/connection.rb
56
61
  - test/fixtures/reference_type.rb
@@ -58,10 +63,14 @@ files:
58
63
  - test/fixtures/reference_types.yml
59
64
  - test/fixtures/reference_codes.yml
60
65
  - test/fixtures/db_definitions
66
+ - test/fixtures/product.rb
67
+ - test/fixtures/product_tariff.rb
68
+ - test/fixtures/tariff.rb
69
+ - test/fixtures/products.yml
70
+ - test/fixtures/tariffs.yml
71
+ - test/fixtures/product_tariffs.yml
61
72
  - test/fixtures/db_definitions/mysql.drop.sql
62
73
  - test/fixtures/db_definitions/mysql.sql
63
- - test/fixtures/db_definitions/mysql2.drop.sql
64
- - test/fixtures/db_definitions/mysql2.sql
65
74
  test_files: []
66
75
 
67
76
  rdoc_options:
@@ -1,2 +0,0 @@
1
- DROP TABLE courses;
2
-
@@ -1,5 +0,0 @@
1
- CREATE TABLE `courses` (
2
- `id` INTEGER NOT NULL PRIMARY KEY,
3
- `name` VARCHAR(255) NOT NULL
4
- ) TYPE=InnoDB;
5
-