fixture_overlord 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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