fixture_tree 1.0.0
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/lib/fixture_tree.rb +141 -0
- data/lib/fixture_tree/rspec_support.rb +112 -0
- metadata +45 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1dcb06bf0cff5a0bf61e4fb25d05747287af2924
|
4
|
+
data.tar.gz: 5cde802d2ccc3684465aa871fcb3e374fc559fd5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 558ec567790defbd8ca976d7e7fe94918f51c069aa0c0c89b82bd18335761e68b3af2c7321deeab6cf5ae6ba506e133c6a1a30ae83985708d144eb135adf418a
|
7
|
+
data.tar.gz: da234c94add70c0baaf9c08f0ed423265ec2ea146e4154fb843836fc73e4dd536b9446ea307d56fb8ba172d00fbbca9dc594cf8c964bc5c6dce9147983ad3513
|
data/lib/fixture_tree.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# Helper for creating directory hierarchies to use with tests.
|
2
|
+
#
|
3
|
+
# You'll typically create trees with `FixtureTree.create`. If you're using RSpec, put this inside one of your specs:
|
4
|
+
#
|
5
|
+
# around do |example|
|
6
|
+
# FixtureTree.create do |tree|
|
7
|
+
# @tree = tree
|
8
|
+
# example.run
|
9
|
+
# end
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# Now put some data into your tree:
|
13
|
+
#
|
14
|
+
# before do
|
15
|
+
# @tree.merge({
|
16
|
+
# 'one' => 'two', # keys can be either strings...
|
17
|
+
# three: 'four', # ...or symbols
|
18
|
+
# five: { # create nested directories by using a hash
|
19
|
+
# six: 'seven'
|
20
|
+
# }
|
21
|
+
# })
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# And test against it:
|
25
|
+
#
|
26
|
+
# it "has a file named 'one' whose contents are 'two'" do
|
27
|
+
# expect(@tree.path.join('one').open.read).to eq('two') # FixtureTree#path returns a Pathname object
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# it "has a nested directory named 'five' with a file named 'six' whose contents are 'seven'" do
|
31
|
+
# expect(@tree.path.join('five/six').open.read).to eq('seven')
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# You can add additional files if a test calls for them:
|
35
|
+
#
|
36
|
+
# it 'lets me add additional files and keeps existing ones around' do
|
37
|
+
# @tree.merge({
|
38
|
+
# 'eight.txt' => 'nine'
|
39
|
+
# })
|
40
|
+
# expect(@tree.path.join('eight.txt').open.read).to eq('nine')
|
41
|
+
# expect(@tree.path.join('one').open.read).to eq('two')
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# Or you can replace the entire tree:
|
45
|
+
#
|
46
|
+
# it 'lets me replace the entire tree' do
|
47
|
+
# @tree.replace({
|
48
|
+
# ten: 'eleven'
|
49
|
+
# })
|
50
|
+
# expect(@tree.path.join('ten').open.read).to eq('eleven')
|
51
|
+
# expect(@tree.path.join('one').exist?).to be(false)
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# The tree will be automatically deleted when your `FixtureTree#create` block exits.
|
55
|
+
class FixtureTree
|
56
|
+
attr_reader :path
|
57
|
+
|
58
|
+
# Wrap the specified `Pathname` instance with a `FixtureTree` that can be used to modify it. No special cleanup will
|
59
|
+
# be undertaken after you're done testing if you create a `FixtureTree` this way, so you'll need to make sure to
|
60
|
+
# delete it after you're done with it.
|
61
|
+
def initialize(path)
|
62
|
+
@path = path
|
63
|
+
end
|
64
|
+
|
65
|
+
# Create an ephemeral `FixtureTree`.
|
66
|
+
#
|
67
|
+
# If a block is given, the tree will be yielded to the block, and the tree's data (including any modifications made
|
68
|
+
# to it during the course of testing) will be deleted when the block exits. If not, `[dir, tree]` will be returned,
|
69
|
+
# where `dir` is a Pathname object pointing to a temporary directory containing the tree and `tree` is the ephemeral
|
70
|
+
# `FixtureTree` object. The tree's contents can be cleaned up by deleting `dir` after testing is complete.
|
71
|
+
def self.create
|
72
|
+
temp_dir = Pathname.new(Dir.mktmpdir('fixture_tree'))
|
73
|
+
tree = FixtureTree.new(temp_dir.join('fixture'))
|
74
|
+
|
75
|
+
if block_given?
|
76
|
+
begin
|
77
|
+
yield FixtureTree.new(temp_dir.join('fixture'))
|
78
|
+
ensure
|
79
|
+
temp_dir.rmtree if temp_dir.exist?
|
80
|
+
end
|
81
|
+
else
|
82
|
+
[temp_dir, tree]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Merge the given directory hierarchy or file into this `FixtureTree`.
|
87
|
+
#
|
88
|
+
# `data` can be either a string or a hash. If it's a hash, this `FixtureTree` will be created as a directory if it's
|
89
|
+
# not already one and a file or directory created for each entry in the hash. Values can themselves be strings or
|
90
|
+
# hashes to create nested files or directories, respectively. If it's a string, this `FixtureTree` will be created
|
91
|
+
# as a file whose contents are the specified string.
|
92
|
+
def merge(data)
|
93
|
+
if data.is_a?(Hash)
|
94
|
+
delete unless @path.directory?
|
95
|
+
@path.mkpath
|
96
|
+
|
97
|
+
data.each do |name, contents|
|
98
|
+
join(name.to_s).merge(contents)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
delete
|
102
|
+
@path.write(data)
|
103
|
+
end
|
104
|
+
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
# Replace this `FixtureTree` with the specified directory hierarchy or file. This is equivalent to calling `delete`
|
109
|
+
# followed by `merge(data)`.
|
110
|
+
def replace(data)
|
111
|
+
delete
|
112
|
+
merge(data)
|
113
|
+
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
117
|
+
# Deletes this `FixtureTree`, if it exists. If this tree is currently a directory, it will be removed along with its
|
118
|
+
# children. If this tree is currently a file, the file will be deleted.
|
119
|
+
#
|
120
|
+
# `merge` or `replace` can later be called to recreate this `FixtureTree`.
|
121
|
+
def delete
|
122
|
+
if @path.directory?
|
123
|
+
@path.rmtree
|
124
|
+
elsif @path.exist?
|
125
|
+
@path.delete
|
126
|
+
end
|
127
|
+
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
# Return a `FixtureTree` offering a view on a nested path of this `FixtureTree`. This can be used like:
|
132
|
+
#
|
133
|
+
# some_tree.join('foo/bar').merge({'baz' => 'qux'})
|
134
|
+
#
|
135
|
+
# to get the same effect as:
|
136
|
+
#
|
137
|
+
# some_tree.merge({'foo' => {'bar' => {'baz' => 'qux'}}})
|
138
|
+
def join(path)
|
139
|
+
FixtureTree.new(@path.join(path))
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
class FixtureTree
|
2
|
+
# RSpec helpers to make testing with FixtureTree even easier.
|
3
|
+
#
|
4
|
+
# Extend RSpecSupport in a test:
|
5
|
+
#
|
6
|
+
# RSpec.describe 'something' do
|
7
|
+
# extend FixtureTree::RSpecSupport
|
8
|
+
#
|
9
|
+
# # specs here
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# Or include it in all your tests:
|
13
|
+
#
|
14
|
+
# RSpec.configure do |c|
|
15
|
+
# c.extend RSpecSupport
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# You can then declare trees using `fixture_tree`:
|
19
|
+
#
|
20
|
+
# RSpec.describe 'something' do
|
21
|
+
# fixture_tree :example_tree, data: {foo: 'bar'}
|
22
|
+
#
|
23
|
+
# it "has a file named 'foo' whose contents are 'bar'" do
|
24
|
+
# expect(example_tree.path.join('foo').open.read).to eq('bar')
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Trees can be populated in `before` hooks, if you like:
|
29
|
+
#
|
30
|
+
# fixture_tree :example_tree
|
31
|
+
#
|
32
|
+
# before(:each) do
|
33
|
+
# example_tree.merge({foo: 'bar'})
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# it "has a file named 'foo' whose contents are 'bar'" do
|
37
|
+
# expect(example_tree.path.join('foo').open.read).to eq('bar')
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Nested contexts can overwrite trees:
|
41
|
+
#
|
42
|
+
# fixture_tree :example_tree, data: {foo: 'bar'}
|
43
|
+
#
|
44
|
+
# it "has a file named 'foo'" do
|
45
|
+
# expect(example_tree.path.children.map(&:basename)).to contain_exactly('foo')
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# context 'with a different set of files' do
|
49
|
+
# fixture_tree :example_tree, data: {baz: 'qux'}
|
50
|
+
#
|
51
|
+
# it "only has a file named 'baz'" do
|
52
|
+
# expect(example_tree.path.children.map(&:basename)).to contain_exactly('baz')
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# Or you can include `merge: true` to merge with the parent context's tree:
|
57
|
+
#
|
58
|
+
# fixture_tree :example_tree, data: {foo: 'bar'}
|
59
|
+
#
|
60
|
+
# it "has a file named 'foo'" do
|
61
|
+
# expect(example_tree.path.children.map(&:basename)).to contain_exactly('foo')
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# context 'with an additional file' do
|
65
|
+
# fixture_tree :example_tree, merge: true, data: {baz: 'qux'}
|
66
|
+
#
|
67
|
+
# it "has files named 'foo' and 'baz'" do
|
68
|
+
# expect(example_tree.path.children.map(&:basename)).to contain_exactly('foo', 'baz')
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# And that's about it.
|
73
|
+
module RSpecSupport
|
74
|
+
def fixture_tree(name, data: nil, merge: false, eager: false)
|
75
|
+
# eager intentionally left undocumented because it's not actually useful until I implement support for running
|
76
|
+
# hooks when a tree is instantiated
|
77
|
+
let(name) do
|
78
|
+
if merge && defined?(super)
|
79
|
+
# asked to merge and super is defined. we'll assume it's a tree from an outer context and merge into it
|
80
|
+
# instead of creating a new tree.
|
81
|
+
tree = super()
|
82
|
+
else
|
83
|
+
# either our parent context doesn't define a tree or we weren't asked to merge with it, so create a new one.
|
84
|
+
temp_dir, tree = FixtureTree.create
|
85
|
+
|
86
|
+
# store the temp dir so that we can clean it up after the test completes
|
87
|
+
instance_variable_set(:"@_fixture_tree_dir_#{name}", temp_dir)
|
88
|
+
end
|
89
|
+
|
90
|
+
if data
|
91
|
+
tree.merge(data)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if eager
|
96
|
+
before do
|
97
|
+
send(name)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
after do
|
102
|
+
# clean up the temp dir if we still know about one
|
103
|
+
if instance_variable_defined?(:"@_fixture_tree_dir_#{name}")
|
104
|
+
# delete it
|
105
|
+
instance_variable_get(:"@_fixture_tree_dir_#{name}").rmtree
|
106
|
+
# then unset it so that we won't accidentally try to clean it up in a parent context
|
107
|
+
remove_instance_variable(:"@_fixture_tree_dir_#{name}")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
metadata
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fixture_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Boyd
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-18 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: aboyd@instructure.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/fixture_tree.rb
|
20
|
+
- lib/fixture_tree/rspec_support.rb
|
21
|
+
homepage: https://github.com/instructure/fixture_tree
|
22
|
+
licenses: []
|
23
|
+
metadata: {}
|
24
|
+
post_install_message:
|
25
|
+
rdoc_options: []
|
26
|
+
require_paths:
|
27
|
+
- lib
|
28
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
requirements: []
|
39
|
+
rubyforge_project:
|
40
|
+
rubygems_version: 2.2.2
|
41
|
+
signing_key:
|
42
|
+
specification_version: 4
|
43
|
+
summary: Helper for creating directory hierarchies to use with tests
|
44
|
+
test_files: []
|
45
|
+
has_rdoc:
|