kanade 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +14 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +7 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +59 -0
  8. data/LICENSE +8 -0
  9. data/README-id.md +81 -0
  10. data/README.md +3 -0
  11. data/ROADMAP.md +11 -0
  12. data/Rakefile +23 -0
  13. data/kanade.gemspec +34 -0
  14. data/lib/kanade/all.rb +14 -0
  15. data/lib/kanade/config/default.rb +10 -0
  16. data/lib/kanade/converter/base.rb +19 -0
  17. data/lib/kanade/converter/big_decimal.rb +19 -0
  18. data/lib/kanade/converter/bool.rb +22 -0
  19. data/lib/kanade/converter/fixnum.rb +15 -0
  20. data/lib/kanade/converter/float.rb +16 -0
  21. data/lib/kanade/converter/list.rb +17 -0
  22. data/lib/kanade/converter/string.rb +17 -0
  23. data/lib/kanade/converter/symbol.rb +20 -0
  24. data/lib/kanade/converter/time.rb +25 -0
  25. data/lib/kanade/dto.rb +47 -0
  26. data/lib/kanade/engine.rb +87 -0
  27. data/lib/kanade/field_info.rb +15 -0
  28. data/lib/kanade/naming_strategy/base.rb +20 -0
  29. data/lib/kanade/naming_strategy/camel_case.rb +14 -0
  30. data/lib/kanade/naming_strategy/pascal_case.rb +12 -0
  31. data/lib/kanade/naming_strategy/snake_case.rb +16 -0
  32. data/lib/kanade/version.rb +3 -0
  33. data/lib/kanade.rb +16 -0
  34. data/spec/converter/string_spec.rb +61 -0
  35. data/spec/fixtures/catalog.rb +5 -0
  36. data/spec/fixtures/catalog_camel_case.json +19 -0
  37. data/spec/fixtures/catalog_pascal_case.json +19 -0
  38. data/spec/fixtures/product.rb +7 -0
  39. data/spec/fixtures/product_wrong.rb +7 -0
  40. data/spec/fixtures/refund_report.json +10 -0
  41. data/spec/fixtures/refund_report.rb +4 -0
  42. data/spec/fixtures/simple_product.json +7 -0
  43. data/spec/integration/class_consistency_spec.rb +49 -0
  44. data/spec/integration/simple_deserialization_integration_spec.rb +58 -0
  45. data/spec/integration/simple_serialization_integration_spec.rb +74 -0
  46. data/spec/matchers/json_matcher.rb +13 -0
  47. data/spec/naming_strategy/camel_case_spec.rb +23 -0
  48. data/spec/spec_helper.rb +106 -0
  49. data/spec/support/deep_hash_compare.rb +1 -0
  50. data/spec/support/json_minify.rb +77 -0
  51. data/spec/support/json_reader.rb +6 -0
  52. metadata +211 -0
@@ -0,0 +1,12 @@
1
+ module Kanade
2
+ module NamingStrategy
3
+ class PascalCase < Base
4
+ Engine.register_name_resolver!(self)
5
+
6
+ def serialize
7
+ end
8
+ def deserialize
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Kanade
2
+ module NamingStrategy
3
+ class SnakeCase < Base
4
+ Engine.register_name_resolver!(self)
5
+
6
+ configurable :always_upcase do |value|
7
+ false unless value.is_a(FalseClass) or value.is_a(TrueClass)
8
+ end
9
+
10
+ def serialize
11
+ end
12
+ def deserialize
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Kanade
2
+ VERSION = '0.1.0.beta1'
3
+ end
data/lib/kanade.rb ADDED
@@ -0,0 +1,16 @@
1
+ # Kanade core
2
+
3
+ if File.exists?(File.join(File.dirname(__FILE__), '..', '.devel'))
4
+ puts ' --> WARNING: You are using development mode of this gem'
5
+ require 'pry'
6
+ end
7
+
8
+ module Kanade
9
+ class NotImplementedException < StandardError; end
10
+ class NotSupportedError < StandardError; end
11
+ end
12
+
13
+ # TODO: Dependency to active support to become optional
14
+ # require 'active_support/core_ext/class/attribute'
15
+ require 'active_support/all'
16
+ require 'kanade/all'
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Kanade::Converter::String do
4
+
5
+ let(:converter) { Kanade::Converter::String.new }
6
+
7
+ context 'can serialize' do
8
+ subject { converter.serialize(term, nil) }
9
+
10
+ context 'nil object' do
11
+ let(:term) { nil }
12
+ it { is_expected.to be_nil }
13
+ end
14
+
15
+ context 'nil string' do
16
+ let(:term) { 'nil' }
17
+ it { is_expected.to eq('nil') }
18
+ end
19
+
20
+ context 'real string' do
21
+ let(:term) { 'mantaap' }
22
+ it { is_expected.to eq('mantaap') }
23
+ end
24
+ end
25
+
26
+ context 'can handle value conversion' do
27
+ subject { converter.serialize(term, nil) }
28
+
29
+ context 'test from int 3' do
30
+ let(:term) { 3 }
31
+ it { is_expected.to eq('3') }
32
+ it { is_expected.to_not eq(3) }
33
+ end
34
+
35
+ context 'test from int 100' do
36
+ let(:term) { 100 }
37
+ it { is_expected.to eq('100') }
38
+ it { is_expected.to_not eq(100) }
39
+ end
40
+ end
41
+
42
+ context 'can deserialize' do
43
+ subject { converter.deserialize(term, nil) }
44
+
45
+ context 'string of number' do
46
+ let(:term) { '100' }
47
+ it { is_expected.to eq('100') }
48
+ end
49
+
50
+ context 'nil' do
51
+ let(:term) { nil }
52
+ it { is_expected.to be_nil }
53
+ end
54
+
55
+ context 'normal string' do
56
+ let(:term) { 'Mantaaaap' }
57
+ it { is_expected.to eq('Mantaaaap') }
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,5 @@
1
+ class Catalog < Kanade::Dto
2
+ field :id, as: :fixnum
3
+ field :serials, as: :list, of: :fixnum
4
+ field :products, as: :list, of: Product
5
+ end
@@ -0,0 +1,19 @@
1
+ {
2
+ "id": 3,
3
+ "products": [
4
+ {
5
+ "id": 100,
6
+ "name": "Cat Plushie",
7
+ "expireAt": null,
8
+ "price": "19.95",
9
+ "available": true
10
+ },
11
+ {
12
+ "id": 101,
13
+ "name": "Taco",
14
+ "expireAt": "2016-09-21 13:52:44 +0700",
15
+ "price": "5.0",
16
+ "available": true
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "Id": 3,
3
+ "Products": [
4
+ {
5
+ "Id": 100,
6
+ "Name": "Cat Plushie",
7
+ "ExpireAt": null,
8
+ "Price": "19.95",
9
+ "Available": true
10
+ },
11
+ {
12
+ "Id": 101,
13
+ "Name": "Taco",
14
+ "ExpireAt": "2016-09-21 13:52:44 +0700",
15
+ "Price": "5.0",
16
+ "Available": true
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,7 @@
1
+ class Product < Kanade::Dto
2
+ field :id, as: :fixnum
3
+ field :name, as: :string
4
+ field :expire_at, as: :time
5
+ field :price, as: :big_decimal
6
+ field :available, as: :bool
7
+ end
@@ -0,0 +1,7 @@
1
+ class ProductWrong < Kanade::Dto
2
+ field :id, as: :string
3
+ field :name, as: :string
4
+ field :expire_at, as: :time
5
+ field :price, as: :big_decimal
6
+ field :available, as: :bool
7
+ end
@@ -0,0 +1,10 @@
1
+ {
2
+ "ID": 9321,
3
+ "affectedProduct": {
4
+ "id": 451,
5
+ "name": "Digital Camera",
6
+ "expireAt": "2019-03-30T06:52:44+09:00",
7
+ "price": "99.95",
8
+ "available": true
9
+ }
10
+ }
@@ -0,0 +1,4 @@
1
+ class RefundReport
2
+ field :refund_id, as: :fixnum, with: 'ID'
3
+ field :affected_product, as: Product
4
+ end
@@ -0,0 +1,7 @@
1
+ {
2
+ "id": 101,
3
+ "name": "Super Duper Enak",
4
+ "expireAt": "2017-10-10T06:52:44+09:00",
5
+ "price": "11.0",
6
+ "available": null
7
+ }
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ class Udang < Kanade::Dto
4
+ field :shrimp, as: :string
5
+ end
6
+
7
+ class Cumi < Kanade::Dto
8
+ field :edible, as: :bool
9
+ end
10
+
11
+ class Mangga < Kanade::Dto
12
+ field :edible, as: :string
13
+ end
14
+
15
+ class ManggaBali < Mangga
16
+ field :color, as: :string
17
+ field :available, as: :bool
18
+ end
19
+
20
+ RSpec.describe 'Class Consistency' do
21
+
22
+ it 'does not respond to non-responding method' do
23
+ expect(Cumi.new).to respond_to(:edible)
24
+ expect(Udang.new).to respond_to(:shrimp)
25
+ expect(Kanade::Dto.new).to_not respond_to(:shrimp)
26
+ expect(Kanade::Dto.new).to_not respond_to(:edible)
27
+ expect(Mangga.new).to_not respond_to(:color)
28
+ end
29
+
30
+ it 'behave with correct field' do
31
+ cumi = Cumi.new
32
+ cumi.edible = true
33
+ mangga = Mangga.new
34
+ mangga.edible = 'yes'
35
+
36
+ expect(cumi.edible).to eq(true)
37
+ expect(mangga.edible).to eq('yes')
38
+ end
39
+
40
+ it 'behave with correct inheritance' do
41
+ m = ManggaBali.new
42
+ m.available = 'true'
43
+ m.edible = 'yes'
44
+
45
+ expect(m.available).to eq(true)
46
+ expect(m.edible).to eq('yes')
47
+ end
48
+
49
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+ require_relative '../fixtures/product'
3
+ require_relative '../fixtures/product_wrong'
4
+
5
+ RSpec.describe 'Deserialization Integration' do
6
+
7
+ before(:all) do
8
+ # Make sure everything has been cleaned up
9
+ raise StandardError unless @engine.nil?
10
+
11
+ @engine = Kanade::Engine.new
12
+ @engine.configure do |config|
13
+ config.contract = :camel_case
14
+ config.enum = :upper_snake_case
15
+ end
16
+ end
17
+
18
+ context 'Single Product' do
19
+
20
+ let(:object) do
21
+ @engine.deserialize(Product, json_of(:simple_product))
22
+ end
23
+
24
+ context 'correct id' do
25
+ subject { object.id }
26
+ it { is_expected.to eq(101) }
27
+ it { is_expected.not_to eq('101') }
28
+ end
29
+
30
+ context 'correct name' do
31
+ subject { object.name }
32
+ it { is_expected.to eq('Super Duper Enak') }
33
+ end
34
+
35
+ context 'correct time conversion' do
36
+ subject { object.expire_at }
37
+ it { is_expected.to eq(Time.parse('2017-10-10T06:52:44+09:00')) }
38
+ it { is_expected.to_not eq(Time.parse('2017-10-10T06:52:44+08:00')) }
39
+ it { is_expected.to_not eq(Time.parse('2017-10-09T19:52:44+00:00')) }
40
+ end
41
+
42
+ context 'correct price' do
43
+ subject { object.price }
44
+ it { is_expected.to eq(BigDecimal.new(11))}
45
+ it { is_expected.to eq(BigDecimal.new('11.0'))}
46
+ it { is_expected.to_not eq(BigDecimal.new('1.1'))}
47
+ end
48
+
49
+ context 'correct availability' do
50
+ subject { object.available }
51
+ it { is_expected.to be_nil }
52
+ end
53
+ end
54
+
55
+ context 'Array of products' do
56
+
57
+ end
58
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require_relative '../fixtures/product'
3
+ require_relative '../fixtures/product_wrong'
4
+
5
+ RSpec.describe 'Serialization Integration' do
6
+
7
+ before(:all) do
8
+ # Make sure everything has been cleaned up
9
+ raise StandardError unless @engine.nil?
10
+
11
+ @engine = Kanade::Engine.new
12
+ @engine.configure do |config|
13
+ config.contract = :camel_case
14
+ config.enum = :upper_snake_case
15
+ end
16
+ end
17
+
18
+ context 'Single Product' do
19
+
20
+ def populate(product)
21
+ product.id = 101
22
+ product.name = 'Super Duper Enak'
23
+ # TODO Do not use ISO8601. Natural date seems broken in Ruby,
24
+ # but serialization should be seamless. It should work on
25
+ # our own, too!
26
+ # product.expire_at = 'Tue, 10 Oct 2017 06:52:44 GMT+9'
27
+ product.expire_at = '2017-10-10T06:52:44+09:00'
28
+ product.available = nil
29
+ product.price = 11
30
+
31
+ product
32
+ end
33
+
34
+ let(:target) do
35
+ populate(Product.new)
36
+ end
37
+
38
+ let(:target_wrong) do
39
+ populate(ProductWrong.new)
40
+ end
41
+
42
+ it 'can serialize correctly' do
43
+ result = @engine.serialize(target)
44
+ expect(result).to be_json_of(:simple_product)
45
+ end
46
+
47
+ it 'can handle value conversion' do
48
+ target.id = "101"
49
+ result = @engine.serialize(target)
50
+ expect(result).to be_json_of(:simple_product)
51
+ end
52
+
53
+ it 'should be fail when a field is set null' do
54
+ target.price = nil
55
+ result = @engine.serialize(target)
56
+ expect(result).to_not be_json_of(:simple_product)
57
+ end
58
+
59
+ it 'should be fail when a field is set differently' do
60
+ target.price = 3232
61
+ result = @engine.serialize(target)
62
+ expect(result).to_not be_json_of(:simple_product)
63
+ end
64
+
65
+ it 'should be fail when data type is wrong' do
66
+ result = @engine.serialize(target_wrong)
67
+ expect(result).to_not be_json_of(:simple_product)
68
+ end
69
+ end
70
+
71
+ context 'Array of products' do
72
+
73
+ end
74
+ end
@@ -0,0 +1,13 @@
1
+ require 'rspec/expectations'
2
+
3
+ RSpec::Matchers.define :be_json_of do |fixture_name|
4
+ path = File.join(File.dirname(__FILE__), '..', 'fixtures', "#{fixture_name.to_s}.json")
5
+ expected_json = JSON.minify File.read path
6
+ match do |actual|
7
+ # return false unless actual.is_a?(String)
8
+ actual.strip == expected_json.strip
9
+ end
10
+ failure_message do |actual|
11
+ "Expecting '#{expected_json.strip}', but got '#{actual.strip}'"
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Kanade::NamingStrategy::CamelCase do
4
+ subject do
5
+ Kanade::NamingStrategy::CamelCase.new
6
+ end
7
+
8
+ it 'can serialize' do
9
+ expect(subject.serialize(:camel_case)).to eq('camelCase')
10
+ expect(subject.serialize(:oneword)).to eq('oneword')
11
+ end
12
+
13
+ it 'can deserialize' do
14
+ expect(subject.deserialize('camelCase')).to eq(:camel_case)
15
+ expect(subject.deserialize('oneword')).to eq(:oneword)
16
+ end
17
+
18
+ it 'does not understand dictionary' do
19
+ expect(subject.serialize(:xml_serializer)).to eq('xmlSerializer')
20
+ expect(subject.deserialize('xmlSerializer')).to eq(:xml_serializer)
21
+ expect(subject.deserialize('XMLSerializer')).to eq(:xml_serializer)
22
+ end
23
+ end
@@ -0,0 +1,106 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+
20
+ require 'kanade'
21
+ require_relative 'matchers/json_matcher'
22
+ require_relative 'support/json_minify'
23
+ require_relative 'support/json_reader'
24
+
25
+ RSpec.configure do |config|
26
+ # rspec-expectations config goes here. You can use an alternate
27
+ # assertion/expectation library such as wrong or the stdlib/minitest
28
+ # assertions if you prefer.
29
+ config.disable_monkey_patching!
30
+
31
+ config.expect_with :rspec do |expectations|
32
+ # This option will default to `true` in RSpec 4. It makes the `description`
33
+ # and `failure_message` of custom matchers include text for helper methods
34
+ # defined using `chain`, e.g.:
35
+ # be_bigger_than(2).and_smaller_than(4).description
36
+ # # => "be bigger than 2 and smaller than 4"
37
+ # ...rather than:
38
+ # # => "be bigger than 2"
39
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
40
+ end
41
+
42
+ # rspec-mocks config goes here. You can use an alternate test double
43
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
44
+ config.mock_with :rspec do |mocks|
45
+ # Prevents you from mocking or stubbing a method that does not exist on
46
+ # a real object. This is generally recommended, and will default to
47
+ # `true` in RSpec 4.
48
+ mocks.verify_partial_doubles = true
49
+ end
50
+
51
+ config.include JsonReader
52
+
53
+ # The settings below are suggested to provide a good initial experience
54
+ # with RSpec, but feel free to customize to your heart's content.
55
+ =begin
56
+ # These two settings work together to allow you to limit a spec run
57
+ # to individual examples or groups you care about by tagging them with
58
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
59
+ # get run.
60
+ config.filter_run :focus
61
+ config.run_all_when_everything_filtered = true
62
+
63
+ # Allows RSpec to persist some state between runs in order to support
64
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
65
+ # you configure your source control system to ignore this file.
66
+ config.example_status_persistence_file_path = "spec/examples.txt"
67
+
68
+ # Limits the available syntax to the non-monkey patched syntax that is
69
+ # recommended. For more details, see:
70
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
71
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
72
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
73
+ config.disable_monkey_patching!
74
+
75
+ # This setting enables warnings. It's recommended, but in some cases may
76
+ # be too noisy due to issues in dependencies.
77
+ config.warnings = true
78
+
79
+ # Many RSpec users commonly either run the entire suite or an individual
80
+ # file, and it's useful to allow more verbose output when running an
81
+ # individual spec file.
82
+ if config.files_to_run.one?
83
+ # Use the documentation formatter for detailed output,
84
+ # unless a formatter has already been configured
85
+ # (e.g. via a command-line flag).
86
+ config.default_formatter = 'doc'
87
+ end
88
+
89
+ # Print the 10 slowest examples and example groups at the
90
+ # end of the spec run, to help surface which specs are running
91
+ # particularly slow.
92
+ config.profile_examples = 10
93
+
94
+ # Run specs in random order to surface order dependencies. If you find an
95
+ # order dependency and want to debug it, you can fix the order by providing
96
+ # the seed, which is printed after each run.
97
+ # --seed 1234
98
+ config.order = :random
99
+
100
+ # Seed global randomization in this process using the `--seed` CLI option.
101
+ # Setting this allows you to use `--seed` to deterministically reproduce
102
+ # test failures related to randomization by passing the same `--seed` value
103
+ # as the one that triggered the failure.
104
+ Kernel.srand config.seed
105
+ =end
106
+ end
@@ -0,0 +1 @@
1
+ deep_hash_compare.rb
@@ -0,0 +1,77 @@
1
+ # Copyright (c) 2014 Geoff Youngs
2
+ # MIT License
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ # https://github.com/geoffyoungs/json-minify-rb
24
+ #
25
+ module JSON
26
+ module Minify
27
+ def self.included(mod)
28
+ mod.send(:extend, self)
29
+ end
30
+ def minify(str)
31
+ ss, buf = StringScanner.new(str), ''
32
+
33
+ until ss.eos?
34
+ # Remove whitespace
35
+ if s = ss.scan(/\s+/)
36
+
37
+ # Scan punctuation
38
+ elsif s = ss.scan(/[{},:\]\[]/)
39
+ buf << s
40
+
41
+ # Scan strings
42
+ elsif s = ss.scan(/""|(".*?[^\\])?"/)
43
+ buf << s
44
+
45
+ # Scan reserved words
46
+ elsif s = ss.scan(/(true|false|null)/)
47
+ buf << s
48
+
49
+ # Scan numbers
50
+ elsif s = ss.scan(/-?\d+([.]\d+)?([eE][-+]?[0-9]+)?/)
51
+ buf << s
52
+
53
+ # Remove C++ style comments
54
+ elsif s = ss.scan(%r<//>)
55
+ ss.scan_until(/(\r?\n|$)/)
56
+
57
+ # Remove C style comments
58
+ elsif s = ss.scan(%r'/[*]')
59
+ ss.scan_until(%r'[*]/') or raise SyntaxError, "Unterminated /*...*/ comment - #{ss.rest}"
60
+
61
+ # Anything else is invalid JSON
62
+ else
63
+ raise SyntaxError, "Unable to pre-scan string: #{ss.rest}"
64
+ end
65
+ end
66
+ buf
67
+ end
68
+
69
+ def minify_parse(buf)
70
+ JSON.parse(minify(buf))
71
+ end
72
+ end
73
+ end
74
+
75
+ module JSON
76
+ include JSON::Minify
77
+ end
@@ -0,0 +1,6 @@
1
+ module JsonReader
2
+ def json_of(fixture_name)
3
+ path = File.join(File.dirname(__FILE__), '..', 'fixtures', "#{fixture_name.to_s}.json")
4
+ json = File.read path
5
+ end
6
+ end