fixture_overlord 0.1.8
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/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +3 -0
- data/License.txt +22 -0
- data/Rakefile +9 -0
- data/Readme.mkd +70 -0
- data/bin/bundler +16 -0
- data/bin/erubis +16 -0
- data/bin/rackup +16 -0
- data/bin/rails +16 -0
- data/bin/rake +16 -0
- data/bin/ri +16 -0
- data/bin/sprockets +16 -0
- data/bin/thor +16 -0
- data/bin/tilt +16 -0
- data/bin/tt +16 -0
- data/fixture_overlord.gemspec +26 -0
- data/lib/fixture_overlord/fixture_accessor.rb +40 -0
- data/lib/fixture_overlord/hashish.rb +75 -0
- data/lib/fixture_overlord/helpers.rb +35 -0
- data/lib/fixture_overlord/mock.rb +132 -0
- data/lib/fixture_overlord/model.rb +55 -0
- data/lib/fixture_overlord/object_coercion.rb +7 -0
- data/lib/fixture_overlord/read_fixture.rb +62 -0
- data/lib/fixture_overlord/string_extension.rb +29 -0
- data/lib/fixture_overlord/version.rb +10 -0
- data/lib/fixture_overlord.rb +17 -0
- data/test/fixture_accessor_test.rb +97 -0
- data/test/fixture_overlord_test.rb +4 -0
- data/test/fixtures/company.yml +3 -0
- data/test/fixtures/error.yaml +2 -0
- data/test/fixtures/features.yml +2 -0
- data/test/fixtures/hotel.yml +7 -0
- data/test/fixtures/person.yaml +7 -0
- data/test/fixtures/phone.yml +7 -0
- data/test/hashish_test.rb +56 -0
- data/test/mock_test.rb +65 -0
- data/test/model_test.rb +72 -0
- data/test/read_fixture_test.rb +41 -0
- data/test/test_helper.rb +6 -0
- metadata +165 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'ostruct'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
# TODO: refactor to a class
|
6
|
+
module FixtureOverlord
|
7
|
+
module Helpers
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# reading the yaml filename
|
11
|
+
def yaml_filename(file)
|
12
|
+
::File.basename(file).split('.').first
|
13
|
+
end
|
14
|
+
|
15
|
+
# take the yaml filename and convert it to a model classname
|
16
|
+
def to_model(file)
|
17
|
+
model = yaml_filename(file).to_s.classify.constantize
|
18
|
+
model
|
19
|
+
end
|
20
|
+
|
21
|
+
# check to see if the model is inherited from ActiveRecord
|
22
|
+
#
|
23
|
+
# TODO: Add more than just ActiveRecord, specifically Sequel
|
24
|
+
#
|
25
|
+
def persisted_model?(file)
|
26
|
+
model = to_model(file)
|
27
|
+
model.respond_to?(:superclass) && model.superclass == ::ActiveRecord::Base
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_to_model_methods?(model)
|
31
|
+
model.respond_to?(:create!) || model.respond_to?(:create) || model.respond_to?(:build)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'securerandom'
|
3
|
+
require_relative 'helpers'
|
4
|
+
require_relative 'hashish'
|
5
|
+
|
6
|
+
module FixtureOverlord
|
7
|
+
class Mock < OpenStruct
|
8
|
+
|
9
|
+
def self.setup(hash)
|
10
|
+
new(hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(hash)
|
14
|
+
super(hash.merge(generate_id))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Converts attributes back to a hash (Hashish).
|
18
|
+
# Beacuse this is still a Hashish Hash, we can covert
|
19
|
+
# it back to a mock.
|
20
|
+
#
|
21
|
+
# ==== Examples
|
22
|
+
#
|
23
|
+
# e.g.
|
24
|
+
# blog.to_attributes => { title: 'Blog' }
|
25
|
+
#
|
26
|
+
def to_attributes
|
27
|
+
Hashish[self.to_h].symbolize_keys
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# add a child association
|
32
|
+
#
|
33
|
+
# ==== Examples
|
34
|
+
#
|
35
|
+
# e.g. (has_many)
|
36
|
+
#
|
37
|
+
# blog.child(posts: post)
|
38
|
+
# blog.posts.first.title
|
39
|
+
#
|
40
|
+
# There are 4 methods aliased to this one to provide
|
41
|
+
# the developer ActiveRecord & a Sequel (ORM) like
|
42
|
+
# interface.
|
43
|
+
#
|
44
|
+
def child(options)
|
45
|
+
associations(options) do |k,v|
|
46
|
+
writer(k,[v])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
alias :has_many :child
|
50
|
+
alias :one_to_many :child
|
51
|
+
|
52
|
+
|
53
|
+
# add a parent association
|
54
|
+
#
|
55
|
+
# ==== Examples
|
56
|
+
#
|
57
|
+
# e.g. (belongs_to)
|
58
|
+
#
|
59
|
+
# post.parent(blog: blog)
|
60
|
+
# post.blog.name
|
61
|
+
#
|
62
|
+
# There are 4 methods aliased to this one to provide
|
63
|
+
# the developer ActiveRecord & a Sequel (ORM) like
|
64
|
+
# interface.
|
65
|
+
#
|
66
|
+
def parent(options)
|
67
|
+
associations(options) do |k,v|
|
68
|
+
writer(k,v)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
alias :belongs_to :parent
|
72
|
+
alias :many_to_one :parent
|
73
|
+
alias :has_one :parent
|
74
|
+
alias :one_to_one :parent
|
75
|
+
|
76
|
+
|
77
|
+
# change a key/value or add one
|
78
|
+
#
|
79
|
+
# ==== Examples
|
80
|
+
#
|
81
|
+
# e.g.
|
82
|
+
# games(:donkey_kong).change(name: 'Jumpman Jr.').mock
|
83
|
+
# games(:donkey_kong).add(leader: 'Jacob').mock
|
84
|
+
#
|
85
|
+
def change(options = {})
|
86
|
+
options.each { |k,v| writer(k,v) }
|
87
|
+
self
|
88
|
+
end
|
89
|
+
alias :add :change
|
90
|
+
|
91
|
+
|
92
|
+
# remove an attribute from the class
|
93
|
+
#
|
94
|
+
# ==== Examples
|
95
|
+
#
|
96
|
+
# e.g.
|
97
|
+
# blog.remove(:name)
|
98
|
+
# blog.name == nil
|
99
|
+
#
|
100
|
+
def remove(key)
|
101
|
+
writer(key)
|
102
|
+
end
|
103
|
+
alias :delete :remove
|
104
|
+
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# generate a unique id and return the hash
|
109
|
+
#
|
110
|
+
def generate_id
|
111
|
+
{ id: ::SecureRandom.random_number(99999) }
|
112
|
+
end
|
113
|
+
|
114
|
+
# assign a value to a hash key
|
115
|
+
#
|
116
|
+
def writer(key, value = nil)
|
117
|
+
self.send("#{key}=", value)
|
118
|
+
end
|
119
|
+
|
120
|
+
def build_model_base
|
121
|
+
@build_model_base ||= Helpers.to_model(yaml_filename)
|
122
|
+
end
|
123
|
+
|
124
|
+
def associations(options, &block)
|
125
|
+
options.each do |k,v|
|
126
|
+
v = Mock.setup(v) if v.is_a?(Hash) || v.is_a?(Hashish)
|
127
|
+
yield k, v
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections.rb'
|
2
|
+
|
3
|
+
module FixtureOverlord
|
4
|
+
class Model
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def init(hash, filename)
|
8
|
+
new(hash, filename).new_model_with_values
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(hash, filename)
|
12
|
+
new(hash, filename).create_as_model
|
13
|
+
end
|
14
|
+
alias_method :create!, :create
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(hash, filename)
|
18
|
+
@hash, @filename = hash, filename
|
19
|
+
end
|
20
|
+
|
21
|
+
def convert_to_model
|
22
|
+
model_name.new( sanitize_hash )
|
23
|
+
end
|
24
|
+
|
25
|
+
def new_model_with_values
|
26
|
+
model_name.new( sanitize_hash )
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_as_model
|
30
|
+
model_name.create( sanitize_hash )
|
31
|
+
end
|
32
|
+
|
33
|
+
def model_name
|
34
|
+
@model_name ||= @filename.to_s.singularize.classify.constantize
|
35
|
+
end
|
36
|
+
|
37
|
+
def can_convert_to_model?
|
38
|
+
model = model_name
|
39
|
+
model.respond_to?(:superclass) &&
|
40
|
+
model.superclass == ::ActiveRecord::Base
|
41
|
+
end
|
42
|
+
|
43
|
+
def respond_to_model_methods?
|
44
|
+
model = convert_to_model
|
45
|
+
model.respond_to?(:create!) ||
|
46
|
+
model.respond_to?(:create) ||
|
47
|
+
model.respond_to?(:build)
|
48
|
+
end
|
49
|
+
|
50
|
+
def sanitize_hash
|
51
|
+
@hash.delete_if { |key,value| key.to_sym == :id }
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'hashish'
|
2
|
+
require 'yaml'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module FixtureOverlord
|
6
|
+
FormattingError = Class.new(StandardError)
|
7
|
+
|
8
|
+
def self.read_fixture(yaml_file, key)
|
9
|
+
ReadFixture.new(yaml_file).read(key)
|
10
|
+
end
|
11
|
+
|
12
|
+
class ReadFixture
|
13
|
+
def initialize(file)
|
14
|
+
@file = file
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(key = nil)
|
18
|
+
hash = read_file
|
19
|
+
hash = hash[key.to_s] unless key.nil?
|
20
|
+
Hashish[hash].symbolize_keys
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# CREDIT: The next three methods are almost identitical to those within Rails'
|
26
|
+
# ActiveRecord FixtureSet::File
|
27
|
+
#
|
28
|
+
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/fixture_set/file.rb
|
29
|
+
#
|
30
|
+
def read_file
|
31
|
+
begin
|
32
|
+
data = ::YAML.load(render)
|
33
|
+
rescue ::ArgumentError, ::Psych::SyntaxError => error
|
34
|
+
raise FormattingError, "a YAML error ocurred parsing #{@file}.\nThe error was:\n #{error.class}: #{error}", error.backtrace
|
35
|
+
end
|
36
|
+
validate(data)
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate(data)
|
40
|
+
unless valid_data?(data)
|
41
|
+
raise FormattingError, "fixture is not a Hash or YAML::Omap."
|
42
|
+
end
|
43
|
+
|
44
|
+
raise FormattingError unless data.all? { |name, row| ::Hash === row }
|
45
|
+
data
|
46
|
+
end
|
47
|
+
|
48
|
+
def render
|
49
|
+
erb_render(
|
50
|
+
IO.read(@file)
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def erb_render(content)
|
55
|
+
::ERB.new(content).result
|
56
|
+
end
|
57
|
+
|
58
|
+
def valid_data?(data)
|
59
|
+
::Hash === data || ::YAML::Omap === data
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
class String
|
3
|
+
def classify
|
4
|
+
if defined?(ActiveSupport::Inflector)
|
5
|
+
ActiveSupport::Inflector.classify(self)
|
6
|
+
else
|
7
|
+
self.gsub(/(_|-)/, ' ').
|
8
|
+
split(' ').each do |word|
|
9
|
+
word.capitalize!
|
10
|
+
end.join
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def constantize
|
15
|
+
if defined?(ActiveSupport::Inflector)
|
16
|
+
ActiveSupport::Inflector.constantize(self)
|
17
|
+
else
|
18
|
+
Object.const_get(self)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def singularize
|
23
|
+
if defined?(ActiveSupport::Inflector)
|
24
|
+
ActiveSupport::Inflector.singularize(self, :en)
|
25
|
+
else
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'yaml'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
require_relative "fixture_overlord/string_extension" unless defined?(ActiveSupport::Inflector)
|
7
|
+
require_relative 'fixture_overlord/fixture_accessor'
|
8
|
+
|
9
|
+
module FixtureOverlord
|
10
|
+
def self.included(klass)
|
11
|
+
klass.extend(ClassMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
include FixtureAccessor
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require './lib/fixture_overlord'
|
3
|
+
|
4
|
+
class Minitest::Test
|
5
|
+
include ::FixtureOverlord
|
6
|
+
fixture_overlord :rule
|
7
|
+
end
|
8
|
+
|
9
|
+
module FixtureOverlord
|
10
|
+
class FixtureAccessorTest < Minitest::Test
|
11
|
+
|
12
|
+
def hash
|
13
|
+
@hash ||= { name: 'Bob', age: 44 }
|
14
|
+
end
|
15
|
+
|
16
|
+
def bob
|
17
|
+
@bob ||= person(:bob).mock
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_hash_fixture
|
21
|
+
assert_equal hash, person(:bob)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_mock_fixture
|
25
|
+
assert bob
|
26
|
+
assert bob.id
|
27
|
+
assert_equal 'Bob', bob.name
|
28
|
+
assert_equal 44, bob.age
|
29
|
+
|
30
|
+
assert_equal hash.merge(id: bob.id), bob.to_attributes
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_mock_association
|
34
|
+
mobile = phone(:bob_mobile).mock
|
35
|
+
shared_mock_association_tests(bob, mobile)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_mock_assocation_when_assocation_is_hash
|
39
|
+
mobile = phone(:bob_mobile)
|
40
|
+
shared_mock_association_tests(bob, mobile)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_mock_association_belongs_to
|
44
|
+
cw = company(:cw).mock
|
45
|
+
assert cw.id
|
46
|
+
assert cw.name
|
47
|
+
assert cw.description
|
48
|
+
|
49
|
+
assert bob.belongs_to(company: cw)
|
50
|
+
assert_equal "Code Wranglers", bob.company.name
|
51
|
+
assert_equal "Custom Software Development", bob.company.description
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_mock_association_belongs_to_when_association_is_hash
|
55
|
+
cw = company(:cw)
|
56
|
+
|
57
|
+
assert bob.belongs_to(company: cw)
|
58
|
+
assert_equal "Code Wranglers", bob.company.name
|
59
|
+
assert_equal "Custom Software Development", bob.company.description
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_changing_a_mock_attribute
|
63
|
+
cw = company(:cw).mock
|
64
|
+
cw.change(name: 'Code Wranglers, Inc.')
|
65
|
+
assert_equal "Code Wranglers, Inc.", cw.name
|
66
|
+
|
67
|
+
cw.change(name: 'Code Wranglers - Software Development')
|
68
|
+
assert_equal 'Code Wranglers - Software Development', cw.name
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_adding_a_new_attribute
|
72
|
+
bob.add(gender: 'male')
|
73
|
+
assert_equal 'male', bob.gender
|
74
|
+
|
75
|
+
bob.change(gender: 'unicorn')
|
76
|
+
assert_equal 'unicorn', bob.gender
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_removing_attributes
|
80
|
+
bob.remove(:name)
|
81
|
+
assert_nil bob.name
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def shared_mock_association_tests(object, assoc)
|
87
|
+
assert object.has_many(phones: assoc)
|
88
|
+
|
89
|
+
assert_equal 18001234567, object.phones.first.number
|
90
|
+
assert_equal 1, object.phones.size
|
91
|
+
assert_equal "cell", object.phones.first.type
|
92
|
+
|
93
|
+
assert object.phones.first.id
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|