fixturama 0.0.5 → 0.0.6
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 +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +51 -0
- data/README.md +33 -0
- data/fixturama.gemspec +1 -1
- data/lib/fixturama/loader/context.rb +30 -0
- data/lib/fixturama/loader/value.rb +38 -0
- data/lib/fixturama/loader.rb +80 -0
- data/lib/fixturama.rb +2 -6
- data/spec/fixturama/load_fixture/_spec.rb +32 -0
- data/spec/spec_helper.rb +6 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0daca457009dcadf8dcc794d28a0694e093671af9f2f17a121066caf45eea73a
|
4
|
+
data.tar.gz: 3e256c4e345e0975b4099f459265915af5abf69c2d34a4b95422e0083e7d26a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b28b4759ba2ca263f0cc956be4dda523197039acb263d436c1a1ac6484aa7f1a08876ded66a3bf13c0e7a3214931f5926bb81d8419965f3d3c6c3156eb1c6a79
|
7
|
+
data.tar.gz: f7e50dd435f64e97e115487ccd462279a72012dec62c346a0a093645220396858eddda13cc02707259d6de5046717f0b43f2eec9ad0dfbce7a6250a5fe7be21a
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
7
7
|
|
8
|
+
## [0.0.6] - [2019-06-09]
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Better matching of YAML/JSON files (nepalez)
|
13
|
+
|
14
|
+
The loader recognizes complex extensions like `data.yml.erb`
|
15
|
+
or `data.json.erb`, as well as `data.YAML` in upper register.
|
16
|
+
|
17
|
+
- Support for Ruby objects (including Activerecord models) serialization
|
18
|
+
in the parameters of fixtures (nepalez)
|
19
|
+
|
20
|
+
You can send objects, that are stringified in a default Ruby way,
|
21
|
+
into fixture loaders (seeds, stubs etc.) via ERB bindings.
|
22
|
+
Those objects will be gracefully inserted into the resulting structure:
|
23
|
+
|
24
|
+
```yaml
|
25
|
+
---
|
26
|
+
:account: <%= user %>
|
27
|
+
```
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
let(:user) { FactoryBot.create :user }
|
31
|
+
subject { load_fixture "#{__dir__}/output.yml", user: user }
|
32
|
+
|
33
|
+
# The `user` object has been bound via ERB
|
34
|
+
it { is_expected.to eq account: user }
|
35
|
+
```
|
36
|
+
|
37
|
+
This feature can be used for adding RSpec [matching arguments](https://relishapp.com/rspec/rspec-mocks/v/3-8/docs/setting-constraints/matching-arguments):
|
38
|
+
|
39
|
+
```yaml
|
40
|
+
---
|
41
|
+
:foo: <%= foo %>
|
42
|
+
:bar: 3
|
43
|
+
```
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
# Use the RSpec `anyting` matcher
|
47
|
+
subject { { foo: 4, bar: 3 } }
|
48
|
+
|
49
|
+
let(:template) { load_fixture "#{__dir__}/template.yml", foo: anyting }
|
50
|
+
|
51
|
+
# The `anyting` has been bound via ERB to the template
|
52
|
+
# Compare `{ foo: 4, bar: 3 }` to the template `{ foo: anything, bar: 3 }`
|
53
|
+
it { is_expected.to include(template) }
|
54
|
+
```
|
55
|
+
|
56
|
+
**Be careful though**: the trick won't work with objects whose default method `Object#to_s` has been overloaded.
|
57
|
+
|
8
58
|
## [0.0.5] - [2018-06-04]
|
9
59
|
|
10
60
|
### Added
|
@@ -82,3 +132,4 @@ This is a first public release with features extracted from production app.
|
|
82
132
|
[0.0.3]: https://github.com/nepalez/fixturama/compare/v0.0.2...v0.0.3
|
83
133
|
[0.0.4]: https://github.com/nepalez/fixturama/compare/v0.0.3...v0.0.4
|
84
134
|
[0.0.5]: https://github.com/nepalez/fixturama/compare/v0.0.4...v0.0.5
|
135
|
+
[0.0.6]: https://github.com/nepalez/fixturama/compare/v0.0.5...v0.0.6
|
data/README.md
CHANGED
@@ -42,6 +42,8 @@ The gem defines 3 helpers (support ERB bindings):
|
|
42
42
|
- `seed_fixture(path_to_yaml, **opts)` to prepare database using the [FactoryBot][factory-bot]
|
43
43
|
- `stub_fixture(path_to_yaml, **opts)` to stub some classes
|
44
44
|
|
45
|
+
### Loading
|
46
|
+
|
45
47
|
```ruby
|
46
48
|
# spec/models/user/_spec.rb
|
47
49
|
RSpec.describe "GraphQL mutation 'deleteProfile'" do
|
@@ -71,6 +73,34 @@ RSpec.describe "GraphQL mutation 'deleteProfile'" do
|
|
71
73
|
end
|
72
74
|
```
|
73
75
|
|
76
|
+
Notice, that since the `v0.0.6` the gem also supports binding any ruby object, not only strings, booleans and numbers:
|
77
|
+
|
78
|
+
```yaml
|
79
|
+
# ./data.yml
|
80
|
+
---
|
81
|
+
account: <%= user %>
|
82
|
+
```
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# Bind activerecord model
|
86
|
+
subject { load_fixture "#{__dir__}/data.yml", user: user }
|
87
|
+
|
88
|
+
let(:user) { FactoryBot.create :user }
|
89
|
+
|
90
|
+
# The same object will be returned
|
91
|
+
it { is_expected.to eq account: user }
|
92
|
+
```
|
93
|
+
|
94
|
+
The object must be named in the options you send to the `load_fixture`, `stub_fixture`, or `seed_fixture` helpers.
|
95
|
+
|
96
|
+
This feature can also be useful to produce a "partially defined" fixtures with [RSpec argument matchers][rspec-argument-matchers]:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
subject { load_fixture "#{__dir__}/data.yml", user: kind_of(ActiveRecord::Base) }
|
100
|
+
```
|
101
|
+
|
102
|
+
### Seeding
|
103
|
+
|
74
104
|
The seed (`seed_fixture`) file should be a YAML/JSON with opinionated parameters, namely:
|
75
105
|
|
76
106
|
- `type` for the name of the [FactoryBot][factory-bot] factory
|
@@ -92,6 +122,8 @@ The seed (`seed_fixture`) file should be a YAML/JSON with opinionated parameters
|
|
92
122
|
|
93
123
|
Use the `count: 2` key to create more objects at once.
|
94
124
|
|
125
|
+
### Stubbing
|
126
|
+
|
95
127
|
Another opinionated format we use for stubs (`stub_fixture`). The gem supports stubbing both message chains and constants.
|
96
128
|
|
97
129
|
For message chains:
|
@@ -181,3 +213,4 @@ The gem is available as open source under the terms of the [MIT License][license
|
|
181
213
|
[factory-bot]: https://github.com/thoughtbot/factory_bot
|
182
214
|
[rspec]: https://rspec.info/
|
183
215
|
[dev_to]: https://dev.to/evilmartians/a-fixture-based-approach-to-interface-testing-in-rails-2cd4
|
216
|
+
[rspec-argument-matchers]: https://relishapp.com/rspec/rspec-mocks/v/3-8/docs/setting-constraints/matching-arguments
|
data/fixturama.gemspec
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
class Fixturama::Loader
|
2
|
+
#
|
3
|
+
# The context of some fixture
|
4
|
+
#
|
5
|
+
class Context
|
6
|
+
# Get value by key
|
7
|
+
# @param [#to_s] key
|
8
|
+
# @return [Object]
|
9
|
+
def [](key)
|
10
|
+
@values.send(key).instance_variable_get(:@value)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def initialize(values)
|
16
|
+
@values = \
|
17
|
+
Hash(values).each_with_object(Hashie::Mash.new) do |(key, val), obj|
|
18
|
+
obj[key] = Value.new(key, val)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(name, *)
|
23
|
+
@values.respond_to?(name) || super
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(name, *args)
|
27
|
+
@values.respond_to?(name) ? @values.send(name, *args) : super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Fixturama::Loader
|
2
|
+
#
|
3
|
+
# Wraps a value with a reference to its key
|
4
|
+
# in the [Fixturama::Loader::Context]
|
5
|
+
#
|
6
|
+
class Value
|
7
|
+
# Regex mather to extract value key from the stringified wrapper
|
8
|
+
MATCHER = /\A\#\<Fixturama::Loader::Context\[([^\]]+)\]\>\z/.freeze
|
9
|
+
|
10
|
+
def self.new(key, value)
|
11
|
+
case value
|
12
|
+
when String, Symbol, Numeric, TrueClass, FalseClass, NilClass then value
|
13
|
+
else super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# The sting representing the value with a reference to it in bindings
|
18
|
+
def to_s
|
19
|
+
"\"#<Fixturama::Loader::Context[#{@key}]>\""
|
20
|
+
end
|
21
|
+
alias to_str to_s
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def initialize(key, value)
|
26
|
+
@key = key
|
27
|
+
@value = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(name, *args, &block)
|
31
|
+
@value.respond_to?(name) ? @value.send(name, *args, &block) : super
|
32
|
+
end
|
33
|
+
|
34
|
+
def respond_to_missing?(name, *)
|
35
|
+
@value.respond_to?(name) || super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#
|
2
|
+
# Load fixture with some options
|
3
|
+
#
|
4
|
+
class Fixturama::Loader
|
5
|
+
require_relative "loader/value"
|
6
|
+
require_relative "loader/context"
|
7
|
+
|
8
|
+
def call
|
9
|
+
return load_yaml if yaml?
|
10
|
+
return load_json if json?
|
11
|
+
|
12
|
+
content
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def initialize(path, opts = {})
|
18
|
+
@path = path
|
19
|
+
@opts = opts.to_h
|
20
|
+
end
|
21
|
+
|
22
|
+
def basename
|
23
|
+
@basename ||= Pathname.new(@path).basename.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def yaml?
|
27
|
+
!basename[YAML_EXTENSION].nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def json?
|
31
|
+
!basename[JSON_EXTENSION].nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def context
|
35
|
+
@context ||= (yaml? || json?) ? Context.new(@opts) : Hashie::Mash.new(@opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
def content
|
39
|
+
bindings = context.instance_eval { binding }
|
40
|
+
content = File.read(@path)
|
41
|
+
|
42
|
+
ERB.new(content).result(bindings)
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_yaml
|
46
|
+
finalize YAML.load(content)
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_json
|
50
|
+
finalize JSON.parse(content)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Takes the nested data loaded from YAML or JSON-formatted fixture,
|
54
|
+
# and serializes its leafs to the corresponding values from a context
|
55
|
+
def finalize(data)
|
56
|
+
case data
|
57
|
+
when Array
|
58
|
+
data.map { |val| finalize(val) }
|
59
|
+
when Hash
|
60
|
+
data.each_with_object({}) { |(key, val), obj| obj[key] = finalize(val) }
|
61
|
+
when String
|
62
|
+
finalize_string(data)
|
63
|
+
else
|
64
|
+
data
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Converts strings of sort `#<Fixturama::Loader::Context[:key]>`
|
69
|
+
# to the corresponding value by the key
|
70
|
+
# @param [String] string
|
71
|
+
# @return [Object]
|
72
|
+
def finalize_string(string)
|
73
|
+
key = string.match(Value::MATCHER)&.captures&.first&.to_s
|
74
|
+
key ? context[key] : string
|
75
|
+
end
|
76
|
+
|
77
|
+
# Matchers for YAML/YML/JSON in file extension like "data.yml.erb" etc.
|
78
|
+
YAML_EXTENSION = /.+\.ya?ml(\.|\z)/i.freeze
|
79
|
+
JSON_EXTENSION = /.+\.json(\.|\z)/i.freeze
|
80
|
+
end
|
data/lib/fixturama.rb
CHANGED
@@ -8,6 +8,7 @@ require "yaml"
|
|
8
8
|
module Fixturama
|
9
9
|
require_relative "fixturama/config"
|
10
10
|
require_relative "fixturama/utils"
|
11
|
+
require_relative "fixturama/loader"
|
11
12
|
require_relative "fixturama/stubs"
|
12
13
|
require_relative "fixturama/seed"
|
13
14
|
|
@@ -28,12 +29,7 @@ module Fixturama
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def load_fixture(path, **opts)
|
31
|
-
|
32
|
-
|
33
|
-
read_fixture(path, **opts).tap do |content|
|
34
|
-
return YAML.load(content) if %w[.yaml .yml].include?(extname)
|
35
|
-
return JSON.parse(content) if extname == ".json"
|
36
|
-
end
|
32
|
+
Loader.new(path, opts).call
|
37
33
|
end
|
38
34
|
|
39
35
|
def read_fixture(path, **opts)
|
@@ -13,9 +13,41 @@ RSpec.describe "load_fixture" do
|
|
13
13
|
it { is_expected.to eq expected }
|
14
14
|
end
|
15
15
|
|
16
|
+
context "YAML with ruby object" do
|
17
|
+
subject { load_fixture("#{__dir__}/data.yaml", id: foobar) }
|
18
|
+
|
19
|
+
before { class Test::Foobar; end }
|
20
|
+
|
21
|
+
let(:foobar) { Test::Foobar.new }
|
22
|
+
let(:expected) { { "foo" => { "bar" => foobar } } }
|
23
|
+
|
24
|
+
it { is_expected.to eq expected }
|
25
|
+
end
|
26
|
+
|
16
27
|
context "JSON" do
|
17
28
|
subject { load_fixture("#{__dir__}/data.json", id: 42) }
|
18
29
|
|
19
30
|
it { is_expected.to eq expected }
|
20
31
|
end
|
32
|
+
|
33
|
+
context "JSON with ruby object" do
|
34
|
+
subject { load_fixture("#{__dir__}/data.json", id: foobar) }
|
35
|
+
|
36
|
+
before { class Test::Foobar; end }
|
37
|
+
|
38
|
+
let(:foobar) { Test::Foobar.new }
|
39
|
+
let(:expected) { { "foo" => { "bar" => foobar } } }
|
40
|
+
|
41
|
+
it { is_expected.to eq expected }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with RSpec argument matchers" do
|
45
|
+
subject { load_fixture("#{__dir__}/data.yaml", id: kind_of(Numeric)) }
|
46
|
+
|
47
|
+
it "loads the matcher", aggregate_failures: true do
|
48
|
+
expect("foo" => { "bar" => 42 }).to include subject
|
49
|
+
expect("foo" => { "bar" => 99 }).to include subject
|
50
|
+
expect("foo" => { "bar" => :a }).not_to include subject
|
51
|
+
end
|
52
|
+
end
|
21
53
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fixturama
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kozin (nepalez)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: factory_bot
|
@@ -114,6 +114,9 @@ files:
|
|
114
114
|
- fixturama.gemspec
|
115
115
|
- lib/fixturama.rb
|
116
116
|
- lib/fixturama/config.rb
|
117
|
+
- lib/fixturama/loader.rb
|
118
|
+
- lib/fixturama/loader/context.rb
|
119
|
+
- lib/fixturama/loader/value.rb
|
117
120
|
- lib/fixturama/rspec.rb
|
118
121
|
- lib/fixturama/seed.rb
|
119
122
|
- lib/fixturama/stubs.rb
|