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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48e09d4c2f2bc2eade2e53915d3a5e6ea365b7af3774da48f72bf9ca30fd1554
4
- data.tar.gz: e451162201f818ca015827b3eb866ac87515730686cffb739fe8ca061e39800e
3
+ metadata.gz: 0daca457009dcadf8dcc794d28a0694e093671af9f2f17a121066caf45eea73a
4
+ data.tar.gz: 3e256c4e345e0975b4099f459265915af5abf69c2d34a4b95422e0083e7d26a8
5
5
  SHA512:
6
- metadata.gz: 52394f8c73eb43946a084e830b0364106b3ce5dedcc9e7155bef17b25b0a7a6df1ccbb55c2252ff64565a64d299b5db7df2c09a2c6bfa2f649c422031126b89a
7
- data.tar.gz: 273c09ddb0ef0f0b3270693c2d3eb38992ae91cd72821e1b0059198b840c8b91946aefe3ae32ca8413deeaaca5691f830fa5a5d9fff64fbd44ab1d8c4c2782aa
6
+ metadata.gz: b28b4759ba2ca263f0cc956be4dda523197039acb263d436c1a1ac6484aa7f1a08876ded66a3bf13c0e7a3214931f5926bb81d8419965f3d3c6c3156eb1c6a79
7
+ data.tar.gz: f7e50dd435f64e97e115487ccd462279a72012dec62c346a0a093645220396858eddda13cc02707259d6de5046717f0b43f2eec9ad0dfbce7a6250a5fe7be21a
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  *.gem
11
11
  .rspec_status
12
+ .idea/
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = "fixturama"
3
- gem.version = "0.0.5"
3
+ gem.version = "0.0.6"
4
4
  gem.author = "Andrew Kozin (nepalez)"
5
5
  gem.email = "andrew.kozin@gmail.com"
6
6
  gem.homepage = "https://github.com/nepalez/fixturama"
@@ -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
- extname = Pathname.new(path).extname
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
@@ -17,4 +17,10 @@ RSpec.configure do |config|
17
17
  config.expect_with :rspec do |c|
18
18
  c.syntax = :expect
19
19
  end
20
+
21
+ config.around do |example|
22
+ module Test; end
23
+ example.run
24
+ Object.send(:remove_const, :Test)
25
+ end
20
26
  end
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.5
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-04 00:00:00.000000000 Z
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