sprig 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +131 -0
- data/Rakefile +5 -0
- data/lib/sprig.rb +29 -0
- data/lib/sprig/configuration.rb +16 -0
- data/lib/sprig/data.rb +6 -0
- data/lib/sprig/dependency.rb +45 -0
- data/lib/sprig/dependency_collection.rb +23 -0
- data/lib/sprig/dependency_sorter.rb +83 -0
- data/lib/sprig/directive.rb +37 -0
- data/lib/sprig/directive_list.rb +30 -0
- data/lib/sprig/helpers.rb +25 -0
- data/lib/sprig/parser.rb +9 -0
- data/lib/sprig/parser/base.rb +15 -0
- data/lib/sprig/parser/csv.rb +22 -0
- data/lib/sprig/parser/google_spreadsheet_json.rb +35 -0
- data/lib/sprig/parser/json.rb +9 -0
- data/lib/sprig/parser/yml.rb +9 -0
- data/lib/sprig/planter.rb +39 -0
- data/lib/sprig/seed.rb +9 -0
- data/lib/sprig/seed/attribute.rb +54 -0
- data/lib/sprig/seed/attribute_collection.rb +33 -0
- data/lib/sprig/seed/entry.rb +80 -0
- data/lib/sprig/seed/factory.rb +56 -0
- data/lib/sprig/seed/record.rb +35 -0
- data/lib/sprig/source.rb +143 -0
- data/lib/sprig/sprig_logger.rb +68 -0
- data/lib/sprig/sprig_record_store.rb +31 -0
- data/lib/sprig/version.rb +3 -0
- data/spec/db/activerecord.db +0 -0
- data/spec/feature/configurations_spec.rb +30 -0
- data/spec/fixtures/cassettes/google_spreadsheet_json_posts.yml +60 -0
- data/spec/fixtures/models/comment.rb +5 -0
- data/spec/fixtures/models/post.rb +2 -0
- data/spec/fixtures/seeds/staging/posts.yml +6 -0
- data/spec/fixtures/seeds/test/comments.yml +6 -0
- data/spec/fixtures/seeds/test/legacy_posts.yml +6 -0
- data/spec/fixtures/seeds/test/posts.csv +2 -0
- data/spec/fixtures/seeds/test/posts.json +1 -0
- data/spec/fixtures/seeds/test/posts.yml +6 -0
- data/spec/fixtures/seeds/test/posts_find_existing_by_multiple.yml +9 -0
- data/spec/fixtures/seeds/test/posts_find_existing_by_single.yml +8 -0
- data/spec/fixtures/seeds/test/posts_missing_dependency.yml +7 -0
- data/spec/lib/sprig/configuration_spec.rb +21 -0
- data/spec/lib/sprig/directive_list_spec.rb +24 -0
- data/spec/lib/sprig/directive_spec.rb +60 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/sprig_spec.rb +205 -0
- metadata +211 -0
data/lib/sprig/source.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
module Sprig
|
2
|
+
class Source
|
3
|
+
|
4
|
+
def initialize(table_name, args = {})
|
5
|
+
@table_name = table_name
|
6
|
+
@args = args
|
7
|
+
end
|
8
|
+
|
9
|
+
def records
|
10
|
+
data[:records] || []
|
11
|
+
end
|
12
|
+
|
13
|
+
def options
|
14
|
+
data[:options] || {}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :table_name, :args
|
20
|
+
|
21
|
+
def data
|
22
|
+
@data ||= begin
|
23
|
+
parser_class.new(source).parse.to_hash.with_indifferent_access
|
24
|
+
ensure
|
25
|
+
source.close
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def source
|
30
|
+
@source ||= begin
|
31
|
+
source = args.fetch(:source) { default_source }
|
32
|
+
|
33
|
+
unless source.respond_to?(:read) && source.respond_to?(:close)
|
34
|
+
raise ArgumentError, 'Data sources must act like an IO.'
|
35
|
+
end
|
36
|
+
|
37
|
+
source
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parser_class
|
42
|
+
@parser_class ||= begin
|
43
|
+
parser_class = args.fetch(:parser) { default_parser_class }
|
44
|
+
|
45
|
+
unless parser_class.method_defined?(:parse)
|
46
|
+
raise ArgumentError, 'Parsers must define #parse.'
|
47
|
+
end
|
48
|
+
|
49
|
+
parser_class
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def default_source
|
54
|
+
File.open(SourceDeterminer.new(table_name).file)
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_parser_class
|
58
|
+
ParserDeterminer.new(source).parser
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
class SourceDeterminer
|
63
|
+
attr_reader :table_name
|
64
|
+
|
65
|
+
def initialize(table_name)
|
66
|
+
@table_name = table_name
|
67
|
+
end
|
68
|
+
|
69
|
+
def file
|
70
|
+
File.new(seed_directory.join(filename))
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
class FileNotFoundError < StandardError; end
|
76
|
+
|
77
|
+
def filename
|
78
|
+
available_files.detect {|name| name =~ /^#{table_name}\./ } || file_not_found
|
79
|
+
end
|
80
|
+
|
81
|
+
def available_files
|
82
|
+
Dir.entries(seed_directory)
|
83
|
+
end
|
84
|
+
|
85
|
+
def file_not_found
|
86
|
+
raise FileNotFoundError,
|
87
|
+
"No datasource file could be found for '#{table_name}'. Try creating "\
|
88
|
+
"#{table_name}.yml, #{table_name}.json, or #{table_name}.csv within "\
|
89
|
+
"#{seed_directory}, or define a custom datasource."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
class ParserDeterminer
|
95
|
+
|
96
|
+
def initialize(file)
|
97
|
+
@file = file
|
98
|
+
end
|
99
|
+
|
100
|
+
def parser
|
101
|
+
match = parser_matchers.detect {|p| p[:extension] =~ extension } || unparsable_file
|
102
|
+
match[:parser]
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
class UnparsableFileError < StandardError; end
|
108
|
+
|
109
|
+
attr_reader :file
|
110
|
+
|
111
|
+
def extension
|
112
|
+
File.extname(file)
|
113
|
+
end
|
114
|
+
|
115
|
+
def parser_matchers
|
116
|
+
[
|
117
|
+
{
|
118
|
+
extension: /\.y(a)?ml/i,
|
119
|
+
parser: Sprig::Parser::Yml
|
120
|
+
},
|
121
|
+
{
|
122
|
+
extension: /\.json/i,
|
123
|
+
parser: Sprig::Parser::Json
|
124
|
+
},
|
125
|
+
{
|
126
|
+
extension: /\.csv/i,
|
127
|
+
parser: Sprig::Parser::Csv
|
128
|
+
}
|
129
|
+
]
|
130
|
+
end
|
131
|
+
|
132
|
+
def parsable_formats
|
133
|
+
['YAML', 'JSON', 'CSV']
|
134
|
+
end
|
135
|
+
|
136
|
+
def unparsable_file
|
137
|
+
raise UnparsableFileError,
|
138
|
+
"No parser was found for the file '#{file}'. Provide a custom parser, or "\
|
139
|
+
"use a supported data format (#{parsable_formats})."
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module Sprig
|
4
|
+
class SprigLogger
|
5
|
+
def initialize
|
6
|
+
@success_count = 0
|
7
|
+
@error_count = 0
|
8
|
+
@errors = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def log_success(seed)
|
12
|
+
message = seed.success_log_text
|
13
|
+
puts green(message)
|
14
|
+
@success_count += 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def log_error(seed)
|
18
|
+
message = seed.error_log_text
|
19
|
+
@errors << seed.record
|
20
|
+
puts red(message)
|
21
|
+
@error_count += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def log_summary
|
25
|
+
puts 'Seeding complete.'
|
26
|
+
|
27
|
+
if @success_count > 0
|
28
|
+
puts green(success_summary)
|
29
|
+
else
|
30
|
+
puts red(success_summary)
|
31
|
+
end
|
32
|
+
|
33
|
+
if @error_count > 0
|
34
|
+
puts red(error_summary)
|
35
|
+
|
36
|
+
@errors.each do |error|
|
37
|
+
puts red("#{error}\n#{error.errors.messages}\n\n")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def processing
|
43
|
+
print "Planting those seeds...\r"
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def colorize(message, color_code)
|
49
|
+
"\e[#{color_code}m#{message}\e[0m"
|
50
|
+
end
|
51
|
+
|
52
|
+
def red(message)
|
53
|
+
colorize(message, 31)
|
54
|
+
end
|
55
|
+
|
56
|
+
def green(message)
|
57
|
+
colorize(message, 32)
|
58
|
+
end
|
59
|
+
|
60
|
+
def success_summary
|
61
|
+
"#{@success_count} #{'seed'.pluralize(@success_count)} successfully planted."
|
62
|
+
end
|
63
|
+
|
64
|
+
def error_summary
|
65
|
+
"#{@error_count} #{'seed'.pluralize(@error_count)} couldn't be planted:"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Sprig
|
4
|
+
class SprigRecordStore
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
class RecordNotFoundError < StandardError;end
|
8
|
+
|
9
|
+
def save(record, sprig_id)
|
10
|
+
records_of_klass(record.class)[sprig_id.to_s] = record
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(klass, sprig_id)
|
14
|
+
records_of_klass(klass)[sprig_id.to_s] || record_not_found
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def records_of_klass(klass)
|
20
|
+
records[klass.name.tableize] ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def records
|
24
|
+
@records ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def record_not_found
|
28
|
+
raise(RecordNotFoundError, "Record for class #{klass} and sprig_id #{sprig_id} could not be found.")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
Binary file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Configurating Sprig" do
|
4
|
+
before do
|
5
|
+
stub_rails_root '~'
|
6
|
+
stub_rails_env 'development'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "can set the directory" do
|
10
|
+
Sprig.configuration.directory.to_path.should == '~/db/seeds/development'
|
11
|
+
|
12
|
+
Sprig.configure do |c|
|
13
|
+
c.directory = 'seed_files'
|
14
|
+
end
|
15
|
+
|
16
|
+
Sprig.configuration.directory.to_path.should == '~/seed_files/development'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can reset the configuration" do
|
20
|
+
Sprig.configure do |c|
|
21
|
+
c.directory = 'seed_files'
|
22
|
+
end
|
23
|
+
|
24
|
+
Sprig.configuration.directory.to_path.should == '~/seed_files/development'
|
25
|
+
|
26
|
+
Sprig.reset_configuration
|
27
|
+
|
28
|
+
Sprig.configuration.directory.to_path.should == '~/db/seeds/development'
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values?alt=json
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- ! '*/*'
|
12
|
+
User-Agent:
|
13
|
+
- Ruby
|
14
|
+
response:
|
15
|
+
status:
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
Content-Type:
|
20
|
+
- application/json; charset=UTF-8
|
21
|
+
Access-Control-Allow-Origin:
|
22
|
+
- ! '*'
|
23
|
+
Expires:
|
24
|
+
- Thu, 02 Jan 2014 22:06:43 GMT
|
25
|
+
Date:
|
26
|
+
- Thu, 02 Jan 2014 22:06:43 GMT
|
27
|
+
Cache-Control:
|
28
|
+
- private, max-age=0, must-revalidate, no-transform
|
29
|
+
Vary:
|
30
|
+
- Accept, X-GData-Authorization, GData-Version
|
31
|
+
Gdata-Version:
|
32
|
+
- '1.0'
|
33
|
+
Last-Modified:
|
34
|
+
- Thu, 02 Jan 2014 21:46:27 GMT
|
35
|
+
Set-Cookie:
|
36
|
+
- NID=67=twk9_9Iqw6h1ZztnpEkt7dtJJIvFCbyeEjQhC5XRwV7jMa4uPJoArpItl5KnmhwPif-owRtCYsUwyChgEMO7GDroZPOFBOV0KvMLzUQmlSn-0tGxTefBSvdVHJ8MIHsi;Domain=.google.com;Path=/;Expires=Fri,
|
37
|
+
04-Jul-2014 22:06:43 GMT;HttpOnly
|
38
|
+
P3p:
|
39
|
+
- CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657
|
40
|
+
for more info."
|
41
|
+
X-Content-Type-Options:
|
42
|
+
- nosniff
|
43
|
+
X-Frame-Options:
|
44
|
+
- SAMEORIGIN
|
45
|
+
X-Xss-Protection:
|
46
|
+
- 1; mode=block
|
47
|
+
Server:
|
48
|
+
- GSE
|
49
|
+
Alternate-Protocol:
|
50
|
+
- 443:quic
|
51
|
+
Transfer-Encoding:
|
52
|
+
- chunked
|
53
|
+
body:
|
54
|
+
encoding: US-ASCII
|
55
|
+
string: ! '{"version":"1.0","encoding":"UTF-8","feed":{"xmlns":"http://www.w3.org/2005/Atom","xmlns$openSearch":"http://a9.com/-/spec/opensearchrss/1.0/","xmlns$gsx":"http://schemas.google.com/spreadsheets/2006/extended","id":{"$t":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values"},"updated":{"$t":"2014-01-02T21:46:27.808Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"Sheet1"},"link":[{"rel":"alternate","type":"text/html","href":"https://spreadsheets.google.com/pub?key\u003d0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E"},{"rel":"http://schemas.google.com/g/2005#feed","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values"},{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values?alt\u003djson"}],"author":[{"name":{"$t":"ryan.foster"},"email":{"$t":"ryan.foster@viget.com"}}],"openSearch$totalResults":{"$t":"1"},"openSearch$startIndex":{"$t":"1"},"entry":[{"id":{"$t":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values/cokwr"},"updated":{"$t":"2014-01-02T21:46:27.808Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"1"},"content":{"type":"text","$t":"title:
|
56
|
+
Google spreadsheet json title, content: Google spreadsheet json content"},"link":[{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values/cokwr"}],"gsx$sprig-id":{"$t":"1"},"gsx$title":{"$t":"Google
|
57
|
+
spreadsheet json title"},"gsx$content":{"$t":"Google spreadsheet json content"}}]}}'
|
58
|
+
http_version:
|
59
|
+
recorded_at: Thu, 02 Jan 2014 22:06:42 GMT
|
60
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1 @@
|
|
1
|
+
{"records":[{"sprig_id":1,"title":"Json title","content":"Json content"}]}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sprig::Configuration do
|
4
|
+
before do
|
5
|
+
stub_rails_root '~'
|
6
|
+
stub_rails_env 'development'
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#directory" do
|
10
|
+
|
11
|
+
it "returns db/seeds/:env by default" do
|
12
|
+
subject.directory.to_path.should == '~/db/seeds/development'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns a custom directory" do
|
16
|
+
subject.directory = 'seed_files'
|
17
|
+
|
18
|
+
subject.directory.to_path.should == '~/seed_files/development'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sprig::DirectiveList do
|
4
|
+
|
5
|
+
describe "#add_seeds_to_hopper" do
|
6
|
+
let(:hopper) { Array.new }
|
7
|
+
let(:directive) { double('directive') }
|
8
|
+
let(:seed_factory) { double('seed_factory') }
|
9
|
+
|
10
|
+
subject { described_class.new(Post) }
|
11
|
+
|
12
|
+
before do
|
13
|
+
Sprig::Directive.stub(:new).with(Post).and_return(directive)
|
14
|
+
|
15
|
+
Sprig::Seed::Factory.stub(:new_from_directive).with(directive).and_return(seed_factory)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "builds seeds from directives and adds to the given array" do
|
19
|
+
seed_factory.should_receive(:add_seeds_to_hopper).with(hopper)
|
20
|
+
|
21
|
+
subject.add_seeds_to_hopper(hopper)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|