data_works 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +529 -0
- data/Rakefile +6 -0
- data/_config.yml +1 -0
- data/bin/_guard-core +17 -0
- data/bin/guard +17 -0
- data/bin/rake +17 -0
- data/bin/rspec +17 -0
- data/data_works.gemspec +35 -0
- data/lib/data_works.rb +15 -0
- data/lib/data_works/base.rb +26 -0
- data/lib/data_works/config.rb +19 -0
- data/lib/data_works/exceptions.rb +6 -0
- data/lib/data_works/grafter.rb +51 -0
- data/lib/data_works/necessary_parent.rb +27 -0
- data/lib/data_works/parent_creator.rb +67 -0
- data/lib/data_works/railtie.rb +17 -0
- data/lib/data_works/relationships.rb +29 -0
- data/lib/data_works/stale_relationship_checker.rb +87 -0
- data/lib/data_works/version.rb +4 -0
- data/lib/data_works/visualization.rb +127 -0
- data/lib/data_works/works.rb +111 -0
- data/lib/tasks/bless.rake +6 -0
- data/spec/adding_records_spec.rb +118 -0
- data/spec/factories/factories.rb +78 -0
- data/spec/helper/data_works_spec_helper.rb +46 -0
- data/spec/helper/test_models.rb +106 -0
- data/spec/helper/test_tables.rb +120 -0
- data/spec/lib/data_faker.rb +37 -0
- data/spec/relationships_spec.rb +108 -0
- data/spec/restricted_parentage_spec.rb +35 -0
- data/spec/singluar_ending_in_es_spec.rb +31 -0
- data/spec/spec_helper.rb +33 -0
- metadata +292 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# data_works needs its own set of tables and ActiveRecord models to test with.
|
4
|
+
require_relative 'test_tables'
|
5
|
+
DataWorks::TestTables.create!
|
6
|
+
require_relative 'test_models'
|
7
|
+
|
8
|
+
module DataWorks
|
9
|
+
class StaleRelationshipChecker
|
10
|
+
def self.check!
|
11
|
+
true # For self testing purposes we can just state that the data model has not changed
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
DataWorks.configure do |config|
|
17
|
+
|
18
|
+
config.necessary_parents = {
|
19
|
+
address: [:pet_profile],
|
20
|
+
agency: [],
|
21
|
+
bell_toy: [{ :pet => :pet_bird }],
|
22
|
+
hooman_toy: [:pet],
|
23
|
+
kind: [],
|
24
|
+
pet: [],
|
25
|
+
pet_bird: [],
|
26
|
+
pet_food: [],
|
27
|
+
pet_profile: [:pet],
|
28
|
+
pet_sitter: [:agency, :kind],
|
29
|
+
pet_sitting_patronage: [:pet_sitter, :pet],
|
30
|
+
tag: [:pet],
|
31
|
+
toy: [:pet],
|
32
|
+
album: [],
|
33
|
+
product: [],
|
34
|
+
picture: [{ :imageable => :product }, :album],
|
35
|
+
}
|
36
|
+
|
37
|
+
config.autocreated_children = {
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
class TheDataWorks < DataWorks::Base
|
42
|
+
end
|
43
|
+
|
44
|
+
RSpec.configure do |config|
|
45
|
+
config.include FactoryGirl::Syntax::Methods
|
46
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
#*******************************************************************************
|
2
|
+
# For testing basic associations.
|
3
|
+
#*******************************************************************************
|
4
|
+
|
5
|
+
class Pet < ActiveRecord::Base
|
6
|
+
has_many :toys
|
7
|
+
has_one :pet_tag
|
8
|
+
has_one :pet_profile
|
9
|
+
has_one :address, through: :pet_profile
|
10
|
+
# We explicitly name the join table so that this code works with both Rails 3 and Rails 4.
|
11
|
+
has_and_belongs_to_many :pet_foods, join_table: 'pet_foods_pets'
|
12
|
+
has_many :pet_sitting_patronages
|
13
|
+
has_many :pet_sitters, through: :pet_sitting_patronages
|
14
|
+
end
|
15
|
+
|
16
|
+
class Toy < ActiveRecord::Base
|
17
|
+
validates :name, length: { minimum: 3 }
|
18
|
+
belongs_to :pet
|
19
|
+
end
|
20
|
+
|
21
|
+
class PetTag < ActiveRecord::Base
|
22
|
+
belongs_to :pet
|
23
|
+
end
|
24
|
+
|
25
|
+
class PetFood < ActiveRecord::Base
|
26
|
+
# Rails 3 expects the join table to be called pet_foods_pets
|
27
|
+
# Rails 4 expects the join table to be called pet_foods_pets
|
28
|
+
# We explicitly name the join table so that this code works with both Rails 3 and Rails 4.
|
29
|
+
has_and_belongs_to_many :pets, join_table: 'pet_foods_pets'
|
30
|
+
end
|
31
|
+
|
32
|
+
class Agency < ActiveRecord::Base
|
33
|
+
has_many :pet_sitters
|
34
|
+
end
|
35
|
+
|
36
|
+
class Kind < ActiveHash::Base
|
37
|
+
self.data = [
|
38
|
+
{:id => 1, :name => "Amateur"},
|
39
|
+
{:id => 2, :name => "Professional"}
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
class PetSitter < ActiveRecord::Base
|
44
|
+
extend ActiveHash::Associations::ActiveRecordExtensions
|
45
|
+
belongs_to :kind
|
46
|
+
belongs_to :agency
|
47
|
+
has_many :pet_sitting_patronages
|
48
|
+
has_many :pets, through: :pet_sitting_patronages
|
49
|
+
end
|
50
|
+
|
51
|
+
class PetSittingPatronage < ActiveRecord::Base
|
52
|
+
belongs_to :pet
|
53
|
+
belongs_to :pet_sitter
|
54
|
+
end
|
55
|
+
|
56
|
+
class PetProfile < ActiveRecord::Base
|
57
|
+
belongs_to :pet
|
58
|
+
has_one :address
|
59
|
+
end
|
60
|
+
|
61
|
+
class Address < ActiveRecord::Base
|
62
|
+
belongs_to :pet_profile
|
63
|
+
end
|
64
|
+
|
65
|
+
#*******************************************************************************
|
66
|
+
# For testing polymorphic associations and custom-named foreign keys.
|
67
|
+
#
|
68
|
+
|
69
|
+
class Picture < ActiveRecord::Base
|
70
|
+
belongs_to :imageable, polymorphic: true
|
71
|
+
belongs_to :album, foreign_key: 'picture_album_id'
|
72
|
+
end
|
73
|
+
|
74
|
+
class Product < ActiveRecord::Base
|
75
|
+
has_many :pictures, as: :imageable
|
76
|
+
end
|
77
|
+
|
78
|
+
class Album < ActiveRecord::Base
|
79
|
+
has_many :pictures, foreign_key: 'picture_album_id'
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
# #*******************************************************************************
|
85
|
+
# # For testing denormalized data structures.
|
86
|
+
# #
|
87
|
+
|
88
|
+
# class Owner < ActiveRecord::Base
|
89
|
+
# has_many :vehicles
|
90
|
+
# has_many :amenities # this is not normalized, you wouldn't normally do this
|
91
|
+
# end
|
92
|
+
|
93
|
+
# class Vehicle < ActiveRecord::Base
|
94
|
+
# has_many :amenities
|
95
|
+
# belongs_to :owner
|
96
|
+
# end
|
97
|
+
|
98
|
+
# class Amenity < ActiveRecord::Base
|
99
|
+
# belongs_to :vehicle
|
100
|
+
# belongs_to :owner # this is not normalized, you wouldn't normally do this
|
101
|
+
# has_one :warranty
|
102
|
+
# end
|
103
|
+
|
104
|
+
# class Warranty < ActiveRecord::Base
|
105
|
+
# belongs_to :amenity
|
106
|
+
# end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module DataWorks
|
2
|
+
class TestTables
|
3
|
+
|
4
|
+
def self.create!
|
5
|
+
return if @already_created_tables
|
6
|
+
ActiveRecord::Migration.class_eval do
|
7
|
+
suppress_messages do
|
8
|
+
|
9
|
+
create_table :agencies, force: true do |t|
|
10
|
+
t.string :name, null: false
|
11
|
+
t.timestamps null: false
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :addresses, force: true do |t|
|
15
|
+
t.string :street, null: false
|
16
|
+
t.string :city, null: false
|
17
|
+
t.string :state, null: false
|
18
|
+
t.integer :pet_profile_id, null: false
|
19
|
+
t.timestamps null: false
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :pet_foods, force: true do |t|
|
23
|
+
t.string :name, null: false
|
24
|
+
t.timestamps null: false
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table :pet_foods_pets, force: true, id: false do |t|
|
28
|
+
t.integer :pet_id, null: false
|
29
|
+
t.integer :pet_food_id, null: false
|
30
|
+
end
|
31
|
+
|
32
|
+
create_table :pet_profiles, force: true do |t|
|
33
|
+
t.string :description, null: false
|
34
|
+
t.string :nickname
|
35
|
+
t.integer :pet_id, null: false
|
36
|
+
t.timestamps null: false
|
37
|
+
end
|
38
|
+
|
39
|
+
create_table :pet_sitters, force: true do |t|
|
40
|
+
t.string :name, null: false
|
41
|
+
t.integer :agency_id, null: false
|
42
|
+
t.integer :kind_id, null: false
|
43
|
+
t.timestamps null: false
|
44
|
+
end
|
45
|
+
|
46
|
+
create_table :pet_sitting_patronages, force: true do |t|
|
47
|
+
t.integer :pet_id, null: false
|
48
|
+
t.integer :pet_sitter_id, null: false
|
49
|
+
t.timestamps null: false
|
50
|
+
end
|
51
|
+
|
52
|
+
create_table :pet_tags, force: true do |t|
|
53
|
+
t.string :registered_name, null: false
|
54
|
+
t.integer :pet_id, null: false
|
55
|
+
t.timestamps null: false
|
56
|
+
end
|
57
|
+
|
58
|
+
create_table :pets, force: true do |t|
|
59
|
+
t.string :name, null: false
|
60
|
+
t.string :kind, null: false
|
61
|
+
t.integer :birth_year
|
62
|
+
t.timestamps null: false
|
63
|
+
end
|
64
|
+
|
65
|
+
create_table :toys, force: true do |t|
|
66
|
+
t.string :name
|
67
|
+
t.string :kind
|
68
|
+
t.integer :pet_id
|
69
|
+
t.timestamps null: false
|
70
|
+
end
|
71
|
+
|
72
|
+
create_table :albums, force: true do |t|
|
73
|
+
t.string :name, null: false
|
74
|
+
t.timestamps null: false
|
75
|
+
end
|
76
|
+
|
77
|
+
create_table :pictures, force: true do |t|
|
78
|
+
t.string :name
|
79
|
+
t.integer :imageable_id
|
80
|
+
t.string :imageable_type
|
81
|
+
t.integer :picture_album_id
|
82
|
+
t.timestamps null: false
|
83
|
+
end
|
84
|
+
|
85
|
+
create_table :products, force: true do |t|
|
86
|
+
t.string :name
|
87
|
+
t.timestamps null: false
|
88
|
+
end
|
89
|
+
|
90
|
+
# create_table :amenities, force: true do |t|
|
91
|
+
# t.string :name
|
92
|
+
# t.integer :vehicle_id
|
93
|
+
# t.integer :owner_id
|
94
|
+
# t.timestamps null: false
|
95
|
+
# end
|
96
|
+
|
97
|
+
# create_table :owners, force: true do |t|
|
98
|
+
# t.string :name, null: false
|
99
|
+
# t.timestamps null: false
|
100
|
+
# end
|
101
|
+
|
102
|
+
# create_table :vehicles, force: true do |t|
|
103
|
+
# t.string :name
|
104
|
+
# t.integer :owner_id
|
105
|
+
# t.timestamps null: false
|
106
|
+
# end
|
107
|
+
|
108
|
+
# create_table :warranties, force: true do |t|
|
109
|
+
# t.string :name
|
110
|
+
# t.integer :amenity_id
|
111
|
+
# t.timestamps null: false
|
112
|
+
# end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
@already_created_tables = true
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Anywhere you need fake string data, use this method.
|
2
|
+
def fake_string
|
3
|
+
words = []
|
4
|
+
(rand(2)+2).times { words << DataFaker.hawaiian_word }
|
5
|
+
words.join(' ')
|
6
|
+
end
|
7
|
+
|
8
|
+
# Don't use this method, use fake_string instead.
|
9
|
+
# Ok, you win, in the situation you described fake_word
|
10
|
+
# makes more sense. Here you go, use it if you need to.
|
11
|
+
def fake_word
|
12
|
+
DataFaker.hawaiian_word
|
13
|
+
end
|
14
|
+
|
15
|
+
# Basically, this is the faker gem + a shrink ray.
|
16
|
+
class DataFaker
|
17
|
+
|
18
|
+
HAWAIIAN_VOWELS = %w( a e i o u )
|
19
|
+
|
20
|
+
HAWAIIAN_CONSONANTS = %w( h k l m n p t w )
|
21
|
+
|
22
|
+
def self.hawaiian_syllable
|
23
|
+
s = ''
|
24
|
+
if rand(100) < 90
|
25
|
+
s << HAWAIIAN_CONSONANTS.sample
|
26
|
+
end
|
27
|
+
s << HAWAIIAN_VOWELS.sample
|
28
|
+
s
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.hawaiian_word
|
32
|
+
word = ''
|
33
|
+
(rand(3)+3).times { word << hawaiian_syllable }
|
34
|
+
word
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require_relative "helper/data_works_spec_helper"
|
2
|
+
|
3
|
+
describe DataWorks::Relationships do
|
4
|
+
describe "#necessary_parents_for" do
|
5
|
+
describe "pet" do
|
6
|
+
it "returns an empty collection" do
|
7
|
+
expect(DataWorks::Relationships.necessary_parents_for(:pet)).to be_empty
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "pet_food" do
|
12
|
+
it "returns an empty collection" do
|
13
|
+
expect(DataWorks::Relationships.necessary_parents_for(:pet_food)).to be_empty
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "agency" do
|
18
|
+
it "returns an empty collection" do
|
19
|
+
expect(DataWorks::Relationships.necessary_parents_for(:agency)).to be_empty
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "toy" do
|
24
|
+
it "returns a collection with a single parent object " do
|
25
|
+
expect(DataWorks::Relationships.necessary_parents_for(:toy).count).to eq 1
|
26
|
+
end
|
27
|
+
it "returns a parent object with #association_name => pet" do
|
28
|
+
parents = DataWorks::Relationships.necessary_parents_for(:toy)
|
29
|
+
expect(parents.first.association_name).to eq(:pet)
|
30
|
+
end
|
31
|
+
it "returns a parent object with #model_name => pet" do
|
32
|
+
parents = DataWorks::Relationships.necessary_parents_for(:toy)
|
33
|
+
expect(parents.first.model_name).to eq(:pet)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "tag" do
|
38
|
+
it "returns a collection with a single parent object " do
|
39
|
+
expect(DataWorks::Relationships.necessary_parents_for(:tag).count).to eq 1
|
40
|
+
end
|
41
|
+
it "returns a parent object with #association_name => pet" do
|
42
|
+
parents = DataWorks::Relationships.necessary_parents_for(:tag)
|
43
|
+
expect(parents.first.association_name).to eq(:pet)
|
44
|
+
end
|
45
|
+
it "returns a parent object with #model_name => pet" do
|
46
|
+
parents = DataWorks::Relationships.necessary_parents_for(:tag)
|
47
|
+
expect(parents.first.model_name).to eq(:pet)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "pet_profile" do
|
52
|
+
it "returns a collection with a single parent object " do
|
53
|
+
expect(DataWorks::Relationships.necessary_parents_for(:pet_profile).count).to eq 1
|
54
|
+
end
|
55
|
+
it "returns a parent object with #association_name => pet" do
|
56
|
+
parents = DataWorks::Relationships.necessary_parents_for(:pet_profile)
|
57
|
+
expect(parents.first.association_name).to eq(:pet)
|
58
|
+
end
|
59
|
+
it "returns a parent object with #model_name => pet" do
|
60
|
+
parents = DataWorks::Relationships.necessary_parents_for(:pet_profile)
|
61
|
+
expect(parents.first.model_name).to eq(:pet)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "pet_sitter" do
|
66
|
+
it "returns a collection with a single parent object " do
|
67
|
+
expect(DataWorks::Relationships.necessary_parents_for(:pet_sitter).count).to eq 2
|
68
|
+
end
|
69
|
+
it "returns a parent object with #association_name => agency" do
|
70
|
+
parents = DataWorks::Relationships.necessary_parents_for(:pet_sitter)
|
71
|
+
expect(parents.first.association_name).to eq(:agency)
|
72
|
+
end
|
73
|
+
it "returns a parent object with #model_name => agency" do
|
74
|
+
parents = DataWorks::Relationships.necessary_parents_for(:pet_sitter)
|
75
|
+
expect(parents.first.model_name).to eq(:agency)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "address" do
|
80
|
+
it "returns a collection with a single parent object " do
|
81
|
+
expect(DataWorks::Relationships.necessary_parents_for(:address).count).to eq 1
|
82
|
+
end
|
83
|
+
it "returns a parent object with #association_name => pet_profile" do
|
84
|
+
parents = DataWorks::Relationships.necessary_parents_for(:address)
|
85
|
+
expect(parents.first.association_name).to eq(:pet_profile)
|
86
|
+
end
|
87
|
+
it "returns a parent object with #model_name => pet_profile" do
|
88
|
+
parents = DataWorks::Relationships.necessary_parents_for(:address)
|
89
|
+
expect(parents.first.model_name).to eq(:pet_profile)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "pet_sitting_patronage" do
|
94
|
+
it "returns a collection with two parent objects" do
|
95
|
+
expect(DataWorks::Relationships.necessary_parents_for(:pet_sitting_patronage).count).to eq 2
|
96
|
+
end
|
97
|
+
it "returns an array of parent objects with #association_names => pet_sitter, pet" do
|
98
|
+
parents = DataWorks::Relationships.necessary_parents_for(:pet_sitting_patronage)
|
99
|
+
expect(parents.map(&:association_name)).to contain_exactly(:pet_sitter, :pet)
|
100
|
+
end
|
101
|
+
it "returns an array of parent objects with #model_names => pet_sitter, pet" do
|
102
|
+
parents = DataWorks::Relationships.necessary_parents_for(:pet_sitting_patronage)
|
103
|
+
expect(parents.map(&:model_name)).to contain_exactly(:pet_sitter, :pet)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative "helper/data_works_spec_helper"
|
2
|
+
|
3
|
+
describe 'DataWorks#set_restriction' do
|
4
|
+
let!(:data) { TheDataWorks.new }
|
5
|
+
|
6
|
+
describe 'setting a restriction using the metheod/registry option'do
|
7
|
+
let!(:darth_cuddles) { data.add_pet }
|
8
|
+
let!(:kittylo_ren) { data.add_pet }
|
9
|
+
|
10
|
+
it 'restricts the parent record for new children to the given object' do
|
11
|
+
death_star = data.add_toy
|
12
|
+
data.set_restriction(for_model: :pet, to: kittylo_ren)
|
13
|
+
star_killer = data.add_toy
|
14
|
+
expect(death_star.pet).to eq darth_cuddles
|
15
|
+
expect(star_killer.pet).to eq kittylo_ren
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'setting a restriction using the block option'do
|
20
|
+
let!(:darth_cuddles) { data.add_pet }
|
21
|
+
let!(:kittylo_ren) { data.add_pet }
|
22
|
+
|
23
|
+
it 'restricts the parent record for new children to the given object' do
|
24
|
+
death_star = data.add_toy
|
25
|
+
star_killer = data.set_restriction(for_model: :pet, to: kittylo_ren) do
|
26
|
+
data.add_toy
|
27
|
+
end
|
28
|
+
wookie_doll = data.add_toy
|
29
|
+
expect(death_star.pet).to eq darth_cuddles
|
30
|
+
expect(wookie_doll.pet).to eq darth_cuddles
|
31
|
+
expect(star_killer.pet).to eq kittylo_ren
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|