seedomatic 0.1.0
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.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/lib/seedomatic/runner.rb +47 -0
- data/lib/seedomatic/seeder.rb +48 -0
- data/lib/seedomatic/version.rb +3 -0
- data/lib/seedomatic.rb +11 -0
- data/readme.markdown +2 -0
- data/seedomatic.gemspec +24 -0
- data/spec/lib/seed_o_matic_spec.rb +77 -0
- data/spec/lib/seeder_spec.rb +120 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/my_model.rb +39 -0
- data/spec/support/seed_directory/dir1.yml +4 -0
- data/spec/support/seed_directory/dir2.yml +4 -0
- data/spec/support/single_model.yml +4 -0
- metadata +92 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module SeedOMatic
|
4
|
+
module Runner
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def run (opts = {})
|
8
|
+
results = {}
|
9
|
+
|
10
|
+
files_to_import(opts).each do |file|
|
11
|
+
file_info = load_file(file)
|
12
|
+
|
13
|
+
if should_import?(opts, file_info)
|
14
|
+
results[file_info[:model_name]] = Seeder.new(file_info).import
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
results
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def should_import?(import_options, file_info)
|
24
|
+
(!import_options[:tagged_with] || ([*import_options[:tagged_with]] - [*file_info[:tags]]).empty?) &&
|
25
|
+
(!import_options[:not_tagged_with] || ([*import_options[:not_tagged_with]] & [*file_info[:tags]]).empty?)
|
26
|
+
end
|
27
|
+
|
28
|
+
def files_to_import(options)
|
29
|
+
if options[:file]
|
30
|
+
[options[:file]]
|
31
|
+
else
|
32
|
+
dir = options[:dir] || "config/seeds"
|
33
|
+
Dir.open(dir).map{|f| "#{dir}/#{f}"}.select{|file| File.file? file}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_file(file)
|
38
|
+
data = YAML.load_file(file)
|
39
|
+
{
|
40
|
+
:model_name => data.keys.first,
|
41
|
+
:match_on => data.first[1]['match_on'],
|
42
|
+
:items => data.first[1]['items']
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module SeedOMatic
|
2
|
+
class Seeder
|
3
|
+
|
4
|
+
attr_accessor :model_name, :items, :match_on
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
@model_name = data[:model_name]
|
8
|
+
@items = data[:items]
|
9
|
+
@match_on = [*data[:match_on]]
|
10
|
+
end
|
11
|
+
|
12
|
+
def import
|
13
|
+
new_records = 0
|
14
|
+
updated_records = 0
|
15
|
+
|
16
|
+
items.each do |i|
|
17
|
+
model = model_class.send(create_method, *create_args(i))
|
18
|
+
|
19
|
+
if model.new_record?
|
20
|
+
new_records += 1
|
21
|
+
else
|
22
|
+
updated_records += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
model.attributes = i
|
26
|
+
model.save!
|
27
|
+
end
|
28
|
+
|
29
|
+
{ :count => items.length, :new => new_records, :updated => updated_records}
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def create_method
|
35
|
+
match_on.empty? ? 'new' : "find_or_initialize_by_#{match_on.join('_and_')}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_args(item)
|
39
|
+
match_on.map{|m| item[m] }
|
40
|
+
end
|
41
|
+
|
42
|
+
def model_class
|
43
|
+
return model_name if model_name.is_a? Class
|
44
|
+
model_name.to_s.classify.constantize
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
data/lib/seedomatic.rb
ADDED
data/readme.markdown
ADDED
data/seedomatic.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "seedomatic/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "seedomatic"
|
7
|
+
s.version = Seedomatic::VERSION
|
8
|
+
s.authors = ["Ryan Brunner"]
|
9
|
+
s.email = ["ryan@ryanbrunner.com"]
|
10
|
+
s.homepage = "http://github.com/ryanbrunner/seedomatic"
|
11
|
+
s.summary = %q{Seed-O-Matic makes seeding easier.}
|
12
|
+
s.description = %q{Create repeatable seed fixtures that you can continually use and deploy to multiple environments and tenants.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "seedomatic"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec", '~> 2.9.0'
|
23
|
+
s.add_runtime_dependency "active_support"
|
24
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SeedOMatic do
|
4
|
+
let!(:mock_seeder) { mock(SeedOMatic::Seeder).as_null_object }
|
5
|
+
|
6
|
+
describe "run" do
|
7
|
+
subject { described_class.run opts }
|
8
|
+
|
9
|
+
context "specified file" do
|
10
|
+
let(:opts) { { :file => 'spec/support/single_model.yml' } }
|
11
|
+
|
12
|
+
specify {
|
13
|
+
SeedOMatic::Seeder.should_receive(:new).with(hash_including(:model_name => 'model_name',
|
14
|
+
:items => ['name' => 'Name 1', 'code' => 'code_1'])).and_return(mock_seeder)
|
15
|
+
subject
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
context "specified directory" do
|
20
|
+
let(:opts) { { :dir => 'spec/support/seed_directory' } }
|
21
|
+
|
22
|
+
specify {
|
23
|
+
SeedOMatic::Seeder.should_receive(:new).with(hash_including(:model_name => 'dir1')).and_return(mock_seeder)
|
24
|
+
SeedOMatic::Seeder.should_receive(:new).with(hash_including(:model_name => 'dir2')).and_return(mock_seeder)
|
25
|
+
|
26
|
+
subject
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
context "no options (assumed config / seeds directory)" do
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "should_import?" do
|
36
|
+
subject { SeedOMatic.send(:should_import?, run_options, file_info)}
|
37
|
+
|
38
|
+
describe "tagged_with" do
|
39
|
+
let(:run_options) { { :tagged_with => 'tag1'}}
|
40
|
+
|
41
|
+
context "tag exists" do
|
42
|
+
let(:file_info) { {:tags => ['tag1', 'tag2']}}
|
43
|
+
it { should be_true }
|
44
|
+
end
|
45
|
+
context "tag does not exist" do
|
46
|
+
let(:file_info) { {:tags => ['tag3', 'tag2']}}
|
47
|
+
it { should be_false }
|
48
|
+
end
|
49
|
+
context "multiple tags" do
|
50
|
+
let(:run_options) { { :tagged_with => ['tag1', 'tag2'] }}
|
51
|
+
context "all match" do
|
52
|
+
let(:file_info) { {:tags => ['tag1', 'tag2', 'tag3']}}
|
53
|
+
it { should be_true }
|
54
|
+
end
|
55
|
+
context "some match" do
|
56
|
+
let(:file_info) { {:tags => ['tag4', 'tag2', 'tag3']}}
|
57
|
+
it { should be_false}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
describe "not_tagged_with" do
|
62
|
+
let(:run_options) { { :not_tagged_with => 'tag1'}}
|
63
|
+
context "tag exists" do
|
64
|
+
let(:file_info) { {:tags => ['tag1', 'tag2']}}
|
65
|
+
it { should be_false }
|
66
|
+
end
|
67
|
+
context "tag does not exist" do
|
68
|
+
let(:file_info) { {:tags => ['tag3']}}
|
69
|
+
it { should be_true }
|
70
|
+
end
|
71
|
+
context "no tags" do
|
72
|
+
let(:file_info) { {:tags => []}}
|
73
|
+
it { should be_true }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SeedOMatic::Seeder do
|
4
|
+
let!(:mock_model) { mock(MyModel).as_null_object }
|
5
|
+
|
6
|
+
let(:seeder) { described_class.new(data) }
|
7
|
+
let(:data) { { :model_name => model_name, :items => items, :match_on => match_on } }
|
8
|
+
let(:model_name) { "MyModel" }
|
9
|
+
let(:items) {
|
10
|
+
[{'name' => 'foo', 'code' => 'uniquecode', 'code_category' => 'more_unique'} ]
|
11
|
+
}
|
12
|
+
let(:match_on) { nil }
|
13
|
+
|
14
|
+
describe "import" do
|
15
|
+
subject { seeder.import }
|
16
|
+
|
17
|
+
describe "creating records" do
|
18
|
+
context "single item" do
|
19
|
+
let(:items) { [{'name' => 'foo'}] }
|
20
|
+
specify {
|
21
|
+
MyModel.should_receive(:new).and_return(mock_model)
|
22
|
+
subject
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
context "multiple items" do
|
27
|
+
let(:items) { [{'name' => 'foo'}, {'name' => 'bar'} ] }
|
28
|
+
specify {
|
29
|
+
MyModel.should_receive(:new).twice.and_return(mock_model)
|
30
|
+
subject
|
31
|
+
}
|
32
|
+
it "should set fields properly" do
|
33
|
+
subject
|
34
|
+
MyModel[0].name.should == 'foo'
|
35
|
+
MyModel[1].name.should == 'bar'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "checking uniqueness on a specified field" do
|
41
|
+
context "single field" do
|
42
|
+
let(:match_on) { 'code' }
|
43
|
+
|
44
|
+
specify {
|
45
|
+
MyModel.should_receive(:find_or_initialize_by_code).with('uniquecode').and_return(MyModel.new)
|
46
|
+
subject
|
47
|
+
}
|
48
|
+
end
|
49
|
+
context "multiple fields" do
|
50
|
+
let(:match_on) { ['code', 'code_category'] }
|
51
|
+
|
52
|
+
specify {
|
53
|
+
MyModel.should_receive(:find_or_initialize_by_code_and_code_category).with('uniquecode', 'more_unique').and_return(MyModel.new)
|
54
|
+
subject
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "create_method" do
|
61
|
+
subject { seeder.send(:create_method) }
|
62
|
+
|
63
|
+
context "no matching fields" do
|
64
|
+
let(:match_on) { nil }
|
65
|
+
it { should == 'new' }
|
66
|
+
end
|
67
|
+
|
68
|
+
context "one matching field" do
|
69
|
+
let(:match_on) { 'code' }
|
70
|
+
it { should == 'find_or_initialize_by_code' }
|
71
|
+
end
|
72
|
+
|
73
|
+
context "multiple matching fields" do
|
74
|
+
let(:match_on) { ['code', 'code_category'] }
|
75
|
+
it { should == 'find_or_initialize_by_code_and_code_category' }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "create_args" do
|
80
|
+
subject { seeder.send(:create_args, {'code' => 1, 'code_category' => 2}) }
|
81
|
+
|
82
|
+
context "no matching fields" do
|
83
|
+
let(:match_on) { nil }
|
84
|
+
it { should == [] }
|
85
|
+
end
|
86
|
+
|
87
|
+
context "one matching field" do
|
88
|
+
let(:match_on) { 'code' }
|
89
|
+
it { should == [1] }
|
90
|
+
end
|
91
|
+
|
92
|
+
context "multiple matching fields" do
|
93
|
+
let(:match_on) { ['code', 'code_category'] }
|
94
|
+
it { should == [1,2] }
|
95
|
+
end
|
96
|
+
|
97
|
+
context "order sensitivity" do
|
98
|
+
let(:match_on) { ['code_category', 'code'] }
|
99
|
+
it { should == [2,1] }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "model class" do
|
104
|
+
subject { seeder.send(:model_class) }
|
105
|
+
|
106
|
+
["MyModel", MyModel, :my_model, 'my_model'].each do |name|
|
107
|
+
describe name do
|
108
|
+
let(:model_name) { name }
|
109
|
+
it { should == MyModel }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
["Blah", "blah", :not_a_model].each do |name|
|
114
|
+
describe name do
|
115
|
+
let(:model_name) { name }
|
116
|
+
specify { expect { subject }.to raise_error }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
class MyModel
|
2
|
+
attr_accessor :name, :new_record
|
3
|
+
|
4
|
+
@@models = []
|
5
|
+
|
6
|
+
def initialize(params = {})
|
7
|
+
self.name = params['name']
|
8
|
+
self.new_record = true
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create(params = {})
|
12
|
+
@@models << m = new(params)
|
13
|
+
m.new_record = false
|
14
|
+
m
|
15
|
+
end
|
16
|
+
|
17
|
+
def attributes=(attr)
|
18
|
+
self.name = attr['name']
|
19
|
+
end
|
20
|
+
|
21
|
+
def save!
|
22
|
+
@@models << self
|
23
|
+
new_record = false
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def new_record?
|
28
|
+
@new_record
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.[] (index)
|
32
|
+
@@models[index]
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.models
|
36
|
+
@@models
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: seedomatic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan Brunner
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70246141530860 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.9.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70246141530860
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: active_support
|
27
|
+
requirement: &70246141530440 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70246141530440
|
36
|
+
description: Create repeatable seed fixtures that you can continually use and deploy
|
37
|
+
to multiple environments and tenants.
|
38
|
+
email:
|
39
|
+
- ryan@ryanbrunner.com
|
40
|
+
executables: []
|
41
|
+
extensions: []
|
42
|
+
extra_rdoc_files: []
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- .rspec
|
46
|
+
- Gemfile
|
47
|
+
- Rakefile
|
48
|
+
- lib/seedomatic.rb
|
49
|
+
- lib/seedomatic/runner.rb
|
50
|
+
- lib/seedomatic/seeder.rb
|
51
|
+
- lib/seedomatic/version.rb
|
52
|
+
- readme.markdown
|
53
|
+
- seedomatic.gemspec
|
54
|
+
- spec/lib/seed_o_matic_spec.rb
|
55
|
+
- spec/lib/seeder_spec.rb
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
- spec/support/my_model.rb
|
58
|
+
- spec/support/seed_directory/dir1.yml
|
59
|
+
- spec/support/seed_directory/dir2.yml
|
60
|
+
- spec/support/single_model.yml
|
61
|
+
homepage: http://github.com/ryanbrunner/seedomatic
|
62
|
+
licenses: []
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ! '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project: seedomatic
|
81
|
+
rubygems_version: 1.8.15
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: Seed-O-Matic makes seeding easier.
|
85
|
+
test_files:
|
86
|
+
- spec/lib/seed_o_matic_spec.rb
|
87
|
+
- spec/lib/seeder_spec.rb
|
88
|
+
- spec/spec_helper.rb
|
89
|
+
- spec/support/my_model.rb
|
90
|
+
- spec/support/seed_directory/dir1.yml
|
91
|
+
- spec/support/seed_directory/dir2.yml
|
92
|
+
- spec/support/single_model.yml
|