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.
@@ -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,7 @@
1
+
2
+ module FixtureOverlord
3
+ class ObjectCoercion
4
+ def initialize
5
+ end
6
+ end
7
+ 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,10 @@
1
+
2
+ module FixtureOverlord
3
+ def self.version
4
+ [MAJOR, MINOR, PATCH].join('.')
5
+ end
6
+
7
+ MAJOR = 0
8
+ MINOR = 1
9
+ PATCH = 8
10
+ 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