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