factory_faster 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +62 -0
- data/Rakefile +9 -0
- data/factory_faster.gemspec +25 -0
- data/lib/factory_faster.rb +5 -0
- data/lib/factory_faster/batch.rb +19 -0
- data/lib/factory_faster/data_store.rb +58 -0
- data/lib/factory_faster/faster.rb +91 -0
- data/lib/factory_faster/target.rb +22 -0
- data/lib/factory_faster/version.rb +3 -0
- data/test/test_helper.rb +10 -0
- data/test/unit/data_store_test.rb +38 -0
- data/test/unit/target_test.rb +13 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0ee96ae3acadba8720236440c575e36320ee3a99
|
4
|
+
data.tar.gz: cbb6cd8ed4193db979c0c1e21087e9ebe960595e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7ae8faff33abad9f4c0c64a7dfb9ff40fa8aeec4c8a4bb3e92e84967ea6c9171dacc6cd0106761f148a97b452a92ad902b2674a41ed6756605fd422ab2f26584
|
7
|
+
data.tar.gz: 6814adf53b72789ff20af010d3a2e5a9afeab88cc789bfb5313ec99b6363e9d4b0272aa54560e194de9c41f6480a0d2b0dd59f8b5e5f0fda6c27707dd0947be3
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p353
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Tom Copeland
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# FactoryFaster
|
2
|
+
|
3
|
+
FactoryFaster optimizes your Rails app's FactoryGirl usage by replacing `create` with `build` where it's safe to do so.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's `Gemfile` in the 'development' group:
|
8
|
+
|
9
|
+
gem 'factory_faster'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install factory_faster
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Process just one file with:
|
22
|
+
|
23
|
+
bundle exec script/rails runner "FactoryFaster::Batch.new('test/unit/foo_test.rb').process"
|
24
|
+
|
25
|
+
Or if you want to include the gem but not `require` it in your `Gemfile`:
|
26
|
+
|
27
|
+
bundle exec script/rails runner "require 'factory_faster' ; FactoryFaster::Batch.new('test/unit/foo_test.rb').process"
|
28
|
+
|
29
|
+
Or use a glob to process lots of files:
|
30
|
+
|
31
|
+
bundle exec script/rails runner "FactoryFaster::Batch.new('test/unit/*.rb').process"
|
32
|
+
|
33
|
+
The output will be something like this:
|
34
|
+
|
35
|
+
foo> $ bundle exec script/rails runner "FactoryFaster::Batch.new('test/unit/foo_test.rb').process"
|
36
|
+
Processing test/unit/foo_test.rb
|
37
|
+
Checking target 1 of 2 on line 42
|
38
|
+
Replacing create with build
|
39
|
+
Running test
|
40
|
+
Passed!
|
41
|
+
Checking target 2 of 2 on line 207
|
42
|
+
Replacing create with build
|
43
|
+
Running test
|
44
|
+
Passed!
|
45
|
+
2 of 2 could be replaced, so replacing those
|
46
|
+
|
47
|
+
## BUGS / TODO
|
48
|
+
|
49
|
+
Adding a new test to a file invalidates all the skip line numbers after the addition. Subsequent runs then get both the old and the new line numbers, so gradually more and more lines get skipped.
|
50
|
+
|
51
|
+
FactoryFaster will miss the case there a test case has multiple calls to `Factory.create` and if more than one of then has to be switched to `Factory.build` in order to trigger an error.
|
52
|
+
|
53
|
+
FactoryFaster depends on `sed` to replace the code. It could use plain old Ruby instead.
|
54
|
+
|
55
|
+
FactoryFaster doesn't skip commented out lines.
|
56
|
+
## Contributing
|
57
|
+
|
58
|
+
1. Fork it ( http://github.com/<my-github-username>/factory_faster/fork )
|
59
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
60
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
61
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
62
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'factory_faster/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "factory_faster"
|
8
|
+
spec.version = FactoryFaster::VERSION
|
9
|
+
spec.authors = ["Tom Copeland"]
|
10
|
+
spec.email = ["tom.copeland@livingsocial.com"]
|
11
|
+
spec.summary = %q{FactoryFaster makes your FactoryGirl tests faster.}
|
12
|
+
spec.description = %q{FactoryFaster makes your FactoryGirl tests faster.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "mocha", "~> 0.14.0"
|
24
|
+
spec.add_development_dependency "shoulda"
|
25
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module FactoryFaster
|
2
|
+
|
3
|
+
#
|
4
|
+
# Each row looks like this:
|
5
|
+
# filename|md5 hash|lines to skip
|
6
|
+
# test/unit/foo_test.rb|abcdefabcde|5,12,19,20
|
7
|
+
#
|
8
|
+
class DataStore
|
9
|
+
|
10
|
+
attr_reader :store_file, :records
|
11
|
+
|
12
|
+
def initialize(filename)
|
13
|
+
@store_file = filename
|
14
|
+
load
|
15
|
+
end
|
16
|
+
|
17
|
+
def set(filename, skips)
|
18
|
+
@records[filename] = [signature_for(filename),skips]
|
19
|
+
store
|
20
|
+
end
|
21
|
+
|
22
|
+
def skips_for(filename)
|
23
|
+
(!@records[filename].nil? && !@records[filename][1].nil?) ? @records[filename][1] : []
|
24
|
+
end
|
25
|
+
|
26
|
+
def unchanged?(filename)
|
27
|
+
!@records[filename].nil? && @records[filename][0] == signature_for(filename)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def signature_for(filename)
|
33
|
+
Digest::MD5.hexdigest(File.read(filename))
|
34
|
+
end
|
35
|
+
|
36
|
+
def store
|
37
|
+
File.open(store_file, "w") do |f|
|
38
|
+
@records.each do |filename, data|
|
39
|
+
f.syswrite("#{filename}|#{data[0]}|#{data[1].join(',')}\n")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# in memory data structure is
|
45
|
+
# test/unit/foo_test.rb => [abcdefabcde, [5,12,19.20]]
|
46
|
+
def load
|
47
|
+
@records = {}
|
48
|
+
File.readlines(store_file).each do |line|
|
49
|
+
fields = line.split("|")
|
50
|
+
filename = fields[0]
|
51
|
+
signature = fields[1]
|
52
|
+
skips = !fields[2].nil? ? fields[2].split(',').map(&:to_i) : []
|
53
|
+
@records[filename] = [signature, skips]
|
54
|
+
end unless !File.exists?(store_file)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module FactoryFaster
|
2
|
+
|
3
|
+
class Faster
|
4
|
+
|
5
|
+
attr_reader :filename, :replacement_targets, :data_store
|
6
|
+
|
7
|
+
def initialize(filename)
|
8
|
+
@filename = filename
|
9
|
+
data_store_file = if defined?(Rails)
|
10
|
+
"#{Rails.root}/tmp/factory_faster.txt"
|
11
|
+
else
|
12
|
+
"tmp/factory_faster.txt"
|
13
|
+
end
|
14
|
+
@data_store = DataStore.new(data_store_file)
|
15
|
+
@replacement_targets = load_replacement_targets
|
16
|
+
end
|
17
|
+
|
18
|
+
def process
|
19
|
+
puts "Processing #{filename}"
|
20
|
+
if data_store.unchanged?(filename)
|
21
|
+
puts "Skipping since it hasn't changed since it was last checked"
|
22
|
+
return
|
23
|
+
end
|
24
|
+
if fixable_replacement_targets.empty?
|
25
|
+
puts "Skipping since there are no targets to fix"
|
26
|
+
return
|
27
|
+
end
|
28
|
+
initial_fixable_targets_count = fixable_replacement_targets.size
|
29
|
+
fixable_replacement_targets.select {|t| !t.skip? }.each_with_index do |target, idx|
|
30
|
+
puts "Checking target #{idx+1} of #{initial_fixable_targets_count} on line #{target.line_number}"
|
31
|
+
try_to_fix(target)
|
32
|
+
end
|
33
|
+
passed = fixable_replacement_targets.select {|t| t.passed? }
|
34
|
+
if passed.any?
|
35
|
+
puts "#{passed.size} of #{initial_fixable_targets_count} could be replaced, so replacing those"
|
36
|
+
passed.each do |target|
|
37
|
+
replace_create_with_build(target.line_number)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
data_store.set(filename, replacement_targets.select {|t| t.skip? }.map(&:line_number))
|
41
|
+
end
|
42
|
+
|
43
|
+
def try_to_fix(target)
|
44
|
+
puts "Replacing create with build"
|
45
|
+
replace_create_with_build(target.line_number)
|
46
|
+
puts "Running test"
|
47
|
+
if test_passes?
|
48
|
+
puts "Passed!"
|
49
|
+
target.passed = true
|
50
|
+
else
|
51
|
+
puts "Error, so marking as a skip"
|
52
|
+
target.skip = true
|
53
|
+
end
|
54
|
+
revert_file
|
55
|
+
end
|
56
|
+
|
57
|
+
def revert_file
|
58
|
+
`git checkout -- #{filename}`
|
59
|
+
end
|
60
|
+
|
61
|
+
def fixable_replacement_targets
|
62
|
+
replacement_targets.select {|t| !t.skip? }
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_passes?
|
66
|
+
run_test
|
67
|
+
$?.exitstatus == 0
|
68
|
+
end
|
69
|
+
|
70
|
+
def run_test
|
71
|
+
cmd = "bundle exec ruby #{filename} 2>&1"
|
72
|
+
`#{cmd}`
|
73
|
+
end
|
74
|
+
|
75
|
+
def replace_create_with_build(line_number)
|
76
|
+
sed_line_number = line_number + 1
|
77
|
+
cmd = "sed -i '' '#{sed_line_number}s/FactoryGirl.create/FactoryGirl.build/' #{filename}"
|
78
|
+
`#{cmd}`
|
79
|
+
end
|
80
|
+
|
81
|
+
def load_replacement_targets
|
82
|
+
res = []
|
83
|
+
File.readlines(filename).each_with_index do |line, idx|
|
84
|
+
next unless line.match /FactoryGirl.create/
|
85
|
+
res << Target.new(idx, data_store.skips_for(filename).include?(idx))
|
86
|
+
end
|
87
|
+
res
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module FactoryFaster
|
2
|
+
class Target
|
3
|
+
|
4
|
+
attr_accessor :line_number, :passed, :skip
|
5
|
+
|
6
|
+
def initialize(line_number, skip=false)
|
7
|
+
@line_number = line_number
|
8
|
+
@passed = false
|
9
|
+
@skip = skip
|
10
|
+
end
|
11
|
+
|
12
|
+
def skip?
|
13
|
+
skip
|
14
|
+
end
|
15
|
+
|
16
|
+
def passed?
|
17
|
+
passed
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha/setup'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
Mocha::Configuration.prevent(:stubbing_non_existent_method)
|
9
|
+
|
10
|
+
require 'factory_faster'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class DataStoreTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "load existing data on initialization" do
|
6
|
+
setup_sample_file
|
7
|
+
store = FactoryFaster::DataStore.new(sample_filename)
|
8
|
+
assert_equal 2, store.records.size
|
9
|
+
end
|
10
|
+
|
11
|
+
should "deserialize skips" do
|
12
|
+
setup_sample_file
|
13
|
+
store = FactoryFaster::DataStore.new(sample_filename)
|
14
|
+
assert_equal [5,12,19,20], store.skips_for("test/unit/foo_test.rb")
|
15
|
+
end
|
16
|
+
|
17
|
+
should "be able to set skips" do
|
18
|
+
setup_sample_file
|
19
|
+
File.expects(:read).returns("foo") # MD5 is acbd18db4cc2f85cedef654fccc4a4d8
|
20
|
+
store = FactoryFaster::DataStore.new(sample_filename)
|
21
|
+
store.set("test/unit/bar_test.rb", [1,2])
|
22
|
+
assert_equal [1,2], store.skips_for("test/unit/bar_test.rb")
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def setup_sample_file
|
28
|
+
File.open(sample_filename, "w") do |f|
|
29
|
+
f.syswrite("test/unit/foo_test.rb|abcdefabcde|5,12,19,20\n")
|
30
|
+
f.syswrite("test/unit/bar_test.rb|abcde|\n")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def sample_filename
|
35
|
+
"tmp/factory_faster.txt"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class TargetTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "default passed? to false" do
|
6
|
+
assert !FactoryFaster::Target.new("foo_test.rb").passed?
|
7
|
+
end
|
8
|
+
|
9
|
+
should "allow skip? to be set on initialization" do
|
10
|
+
assert FactoryFaster::Target.new("foo_test.rb", true).skip?
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: factory_faster
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Copeland
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mocha
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.14.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.14.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: shoulda
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: FactoryFaster makes your FactoryGirl tests faster.
|
70
|
+
email:
|
71
|
+
- tom.copeland@livingsocial.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- .ruby-version
|
78
|
+
- CHANGELOG.md
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- factory_faster.gemspec
|
84
|
+
- lib/factory_faster.rb
|
85
|
+
- lib/factory_faster/batch.rb
|
86
|
+
- lib/factory_faster/data_store.rb
|
87
|
+
- lib/factory_faster/faster.rb
|
88
|
+
- lib/factory_faster/target.rb
|
89
|
+
- lib/factory_faster/version.rb
|
90
|
+
- test/test_helper.rb
|
91
|
+
- test/unit/data_store_test.rb
|
92
|
+
- test/unit/target_test.rb
|
93
|
+
homepage: ''
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.0.14
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: FactoryFaster makes your FactoryGirl tests faster.
|
117
|
+
test_files:
|
118
|
+
- test/test_helper.rb
|
119
|
+
- test/unit/data_store_test.rb
|
120
|
+
- test/unit/target_test.rb
|