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