fixation 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 43ab7505aeb50e8b8eb654cc0e5b49194a67666d
4
- data.tar.gz: ec50e822fa5ce18a86a9e2421e456421bacb7217
3
+ metadata.gz: ca6857caa4389c5c93c2be0ad4ddcd79bdd6895b
4
+ data.tar.gz: 3130d2d5b400e597fb04c57fd80a8bb0449e8cc1
5
5
  SHA512:
6
- metadata.gz: 55d2e7768eaef0e82ad0ab9d12d8ada5ec0d57995ddc2c0a63a89c5689f828a37c99c8afb898205015696296d56785c249feb0d9a9cf15a9acc9d2fd17cc0fb3
7
- data.tar.gz: c33f684d567d9654e510dbcc3fa3484dfed8fbc30df56237cfa3b3638213f6f02db25cbaa3dd136a23fcd7414ee9d2893b53be213dc5aa6386c5c6a15b69dbff
6
+ metadata.gz: f589e251617bf4efcb8e6818c35b1e471a06f07ce0dfc605edfa8ed4eeb1f779f29205cd844d609bffdfc22052f331d8cc234ff5d1f9fc158d7ff55752659fba
7
+ data.tar.gz: db7f49521e7a588e71a0d672c168053eba47193b3ada05d9fbc34687465d898bf7aa0f984bcf635c37fdc2cff8f189782ff4a8db3d184f5b4eba2fd1621715f9
data/README.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  This gem will precompile the SQL statements needed to clear and repopulate your test tables with fixtures when the app boots under spring, so that spec startup just needs to run a small number of multi-row SQL statements to prepare for run. This takes around 1/10th the time as a normal fixture load.
4
4
 
5
- To avoid any problems when you change your model classes, Fixation avoids loading model classes when it reads your fixture files. This creates some incompatibilities with normal ActiveRecord fixtures for certain use cases - see Limitations below.
6
-
5
+ Like ActiveRecord's normal fixture implementation, Fixation will load the model classes in order to use metadata about associations and enums. But since Fixation is run at the time spring starts, Fixation will then 'reload' rails (the same way spring does), so you can change your model classes and re-run tests without having to restart spring.
7
6
 
8
7
  ## Installation
9
8
 
@@ -23,39 +22,41 @@ Or install it yourself as:
23
22
 
24
23
  Then, make an initializer:
25
24
 
26
- if ENV['PRELOAD_FIXTURES'].to_i > 0 && Rails.env.test?
27
- Rails.application.config.after_initialize do
28
- require 'spec/fixture_helper'
29
- Fixation.build_fixtures
30
- end
31
- end
25
+ ```ruby
26
+ if Rails.env.test? && Fixation.running_under_spring?
27
+ Rails.application.config.after_initialize do
28
+ Fixation.preload_for_spring
29
+ end
30
+ end
31
+ ```
32
32
 
33
33
  And run `spring stop` so this gets picked up.
34
34
 
35
35
  Finally, open up your spec_helper.rb, and find the current `global_fixtures` setting:
36
36
 
37
- config.global_fixtures = :all
37
+ ```ruby
38
+ config.global_fixtures = :all
39
+ ```
38
40
 
39
41
  Change that to [] so that ActiveRecord doesn't load the fixtures again itself, and load the Fixation module:
40
42
 
41
- config.global_fixtures = []
42
- config.include Fixation.fixture_methods
43
+ ```ruby
44
+ config.global_fixtures = []
45
+ config.include Fixation.fixture_methods
46
+ ```
43
47
 
44
48
  ## Usage
45
49
 
46
- Add the PRECOMPILE_FIXTURES=1 option to your spring test commands:
50
+ Simply use run your tests under spring.
47
51
 
48
- PRECOMPILE_FIXTURES=1 bundle exec spring spec/models/my_spec.rb
52
+ bundle exec spring spec/models/my_spec.rb
49
53
 
50
54
  ## Limitations
51
55
 
52
- PRECOMPILE_FIXTURES is not on by default in our suggested initializer above, because you don't want it set when running rake tasks like `db:create` and `db:test:prepare` - the initializer above will asplode if you do. This is one of the biggest drawbacks of this version of the gem.
56
+ You'll need to run rake tasks like `db:create` and `db:test:prepare` the normal way (without spring), because if the database doesn't exist or the schema isn't loaded, the initializer above will asplode.
53
57
 
54
- Because Fixation wants to avoid loading your model classes when the app initializes, not all features of regular ActiveRecord fixtures are supported:
55
- * the fixture filenames must match the database table names directly - whereas ActiveRecord actually assumes the filenames are underscored versions of the model class names, and it uses that class's configured table name
56
- * when using the identify syntax to set association foreign keys, you must use the name that corresponds to the foreign key attribute (for example, `parent: sue` if you want to set the `parent_id` field to the appropriate value for the fixture called `sue`) - whereas ActiveRecord accepts the name of the association (`lead_carer: sue`) even if it uses a different foreign key name (`belongs_to :lead_carer, :foreign_key => :parent_id`)
58
+ Not all features of regular ActiveRecord fixtures are supported:
57
59
  * HABTM support has not been implemented (does anyone use HABTM these days?)
58
- * enums are not known
59
60
 
60
61
  ## Contributing
61
62
 
data/lib/fixation.rb CHANGED
@@ -14,6 +14,20 @@ module Fixation
14
14
  @table_name = table_name
15
15
  @connection = connection
16
16
  @now = now
17
+
18
+ @klass_name = @table_name.classify
19
+ begin
20
+ @klass = @klass_name.constantize
21
+ @klass = nil unless @klass < ActiveRecord::Base
22
+ rescue NameError
23
+ ActiveRecord::Base.logger.warn "couldn't load #{@klass_name} for fixture table #{table_name}: #{$!}"
24
+ end
25
+
26
+ if @klass
27
+ @primary_key = @klass.primary_key
28
+ @record_timestamps = @klass.record_timestamps
29
+ @inheritance_column = @klass.inheritance_column
30
+ end
17
31
  end
18
32
 
19
33
  def columns_hash
@@ -49,12 +63,8 @@ module Fixation
49
63
 
50
64
  def embellish_fixture(name, attributes, columns_hash)
51
65
  # populate the primary key column, if not already set
52
- if columns_hash['id'] && !attributes.has_key?('id')
53
- attributes['id'] = Fixation.identify(name, columns_hash['id'].type)
54
- end
55
-
56
- if columns_hash['uuid'] && !attributes.has_key?('uuid')
57
- attributes['uuid'] = Fixation.identify(name, columns_hash['uuid'].type)
66
+ if @primary_key && columns_hash[@primary_key] && !attributes.has_key?(@primary_key)
67
+ attributes[@primary_key] = Fixation.identify(name, columns_hash[@primary_key].type)
58
68
  end
59
69
 
60
70
  # substitute $LABEL into all string values
@@ -63,27 +73,50 @@ module Fixation
63
73
  end
64
74
 
65
75
  # populate any timestamp columns, if not already set
66
- %w(created_at updated_at).each do |column_name|
67
- attributes[column_name] = now if columns_hash[column_name] && !attributes.has_key?(column_name)
68
- end
69
- %w(created_at updated_at).each do |column_name|
70
- attributes[column_name] = now.to_date if columns_hash[column_name] && !attributes.has_key?(column_name)
76
+ if @record_timestamps
77
+ %w(created_at updated_at).each do |column_name|
78
+ attributes[column_name] = now if columns_hash[column_name] && !attributes.has_key?(column_name)
79
+ end
80
+ %w(created_at updated_at).each do |column_name|
81
+ attributes[column_name] = now.to_date if columns_hash[column_name] && !attributes.has_key?(column_name)
82
+ end
71
83
  end
72
84
 
73
- # convert any association names into the identity column equivalent
85
+ # convert enum names to values
86
+ @klass.defined_enums.each do |name, values|
87
+ attributes[name] = values.fetch(attributes[name], attributes[name]) if attributes.has_key?(name)
88
+ end if @klass.respond_to?(:defined_enums)
89
+
90
+ # convert any association names into the identity column equivalent - following code from activerecord's fixtures.rb
74
91
  nonexistant_columns = attributes.keys - columns_hash.keys
75
- nonexistant_columns.each do |column_name|
76
- if columns_hash["#{column_name}_id"]
77
- value = attributes.delete(column_name)
78
92
 
79
- if columns_hash["#{column_name}_type"] && value.is_a?(String) && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
80
- # support polymorphic belongs_to as "label (Type)"
81
- attributes["#{column_name}_type"] = $1
93
+ if @klass && nonexistant_columns.present?
94
+ # If STI is used, find the correct subclass for association reflection
95
+ reflection_class =
96
+ if attributes.include?(@inheritance_column)
97
+ attributes[@inheritance_column].constantize rescue @klass
98
+ else
99
+ @klass
82
100
  end
83
101
 
84
- attributes["#{column_name}_id"] = value ? Fixation.identify(value) : value
85
- else
86
- raise ActiveRecord::Fixture::FormatError, "No column named #{column_name} found in table #{table_name}"
102
+ nonexistant_columns.each do |column_name|
103
+ association = reflection_class._reflections[column_name.to_sym]
104
+
105
+ if association.nil?
106
+ raise ActiveRecord::Fixture::FormatError, "No column named #{column_name} found in table #{table_name}"
107
+ elsif association.macro != :belongs_to
108
+ raise ActiveRecord::Fixture::FormatError, "Association #{column_name} in table #{table_name} has type #{association.macro}, which is not currently supported"
109
+ else
110
+ value = attributes.delete(column_name)
111
+
112
+ if association.options[:polymorphic] && value.is_a?(String) && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
113
+ # support polymorphic belongs_to as "label (Type)"
114
+ attributes[association.foreign_type] = $1
115
+ end
116
+
117
+ fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
118
+ attributes[fk_name] = value ? ActiveRecord::FixtureSet.identify(value) : value
119
+ end
87
120
  end
88
121
  end
89
122
  end
@@ -163,9 +196,11 @@ module Fixation
163
196
  end
164
197
 
165
198
  def apply_fixtures(connection = ActiveRecord::Base.connection)
166
- @statements.each do |table_name, table_statements|
167
- table_statements.each do |statement|
168
- connection.execute(statement)
199
+ connection.transaction do
200
+ @statements.each do |table_name, table_statements|
201
+ table_statements.each do |statement|
202
+ connection.execute(statement)
203
+ end
169
204
  end
170
205
  end
171
206
  end
@@ -223,17 +258,13 @@ module Fixation
223
258
  cattr_accessor :paths
224
259
  self.paths = %w(test/fixtures spec/fixtures)
225
260
 
226
- def self.build_fixtures
227
- subclasses_before = ActiveRecord::Base.subclasses
261
+ def self.preload_for_spring
262
+ build_fixtures
263
+ unload_models!
264
+ end
228
265
 
266
+ def self.build_fixtures
229
267
  @fixtures = Fixtures.new
230
-
231
- subclasses_after = ActiveRecord::Base.subclasses
232
-
233
- unless subclasses_after.size == subclasses_before.size
234
- new_subclasses = subclasses_after - subclasses_before
235
- puts "warning: #{new_subclasses.to_sentence} #{new_subclasses.size == 1 ? 'was' : 'were'} auto-loaded while loading fixtures. #{new_subclasses.size == 1 ? 'this class' : 'these classes'} may not reload properly."
236
- end
237
268
  end
238
269
 
239
270
  def self.apply_fixtures
@@ -259,4 +290,20 @@ module Fixation
259
290
  ActiveRecord::FixtureSet.identify(label, column_type)
260
291
  end
261
292
  end
293
+
294
+ def self.running_under_spring?
295
+ defined?(Spring::Application)
296
+ end
297
+
298
+ # reloads Rails (using the code from Spring) in order to unload the model classes that get
299
+ # auto-loaded when we read the fixture definitions.
300
+ def self.unload_models!
301
+ # Rails 5.1 forward-compat. AD::R is deprecated to AS::R in Rails 5.
302
+ if defined? ActiveSupport::Reloader
303
+ Rails.application.reloader.reload!
304
+ else
305
+ ActionDispatch::Reloader.cleanup!
306
+ ActionDispatch::Reloader.prepare!
307
+ end
308
+ end
262
309
  end
@@ -1,3 +1,3 @@
1
1
  module Fixation
2
- VERSION = "0.1.1"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fixation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Bryant
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-24 00:00:00.000000000 Z
11
+ date: 2016-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler