sequel-seed 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 69f6d93c61eea344cdf8319b68f627bc9abf8b97
4
+ data.tar.gz: 7e3042d71cc4c888924bc7603085dd8606e00a2f
5
+ SHA512:
6
+ metadata.gz: b41ae81873403d6688ac393b75859767eac89acbeade98f67724e307c1bf18cf6d51643164581c5baafe07f6b0d4e3b18f76f09b41f6ab22a0256ca3294a92bc
7
+ data.tar.gz: 243e5ae79b93013e95925b20fc98019b78680be3217a4dfa85f3071c37474bb67f515b9ad9c81df5c13f8045fb456bfd47401d17fffa6607b32619832a4f5df3
data/CHANGELOG ADDED
@@ -0,0 +1 @@
1
+ ## Changelog
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Ewerton Assis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Sequel::Seed
2
+
3
+ > A Sequel extension to make seeds/fixtures manageable like migrations
4
+
5
+ ## Usage
6
+
7
+ Load the extension
8
+
9
+ ```rb
10
+ require 'sequel'
11
+ require 'sequel/extensions/seed'
12
+
13
+ Sequel.extension :seed
14
+ ```
15
+
16
+ Create a seed file (eg. `/path/to/seeds/20150928071637_currencies`)
17
+
18
+ ```rb
19
+ Sequel.seed(:development, :test, :production) do
20
+ def run
21
+ [
22
+ ['USD', 'United States dollar'],
23
+ ['BRL', 'Brazilian real']
24
+ ].each do |abbr, name|
25
+ Currency.create abbr: abbr, name: name
26
+ end
27
+ end
28
+ end
29
+ ```
30
+
31
+ Set the environment
32
+
33
+ ```rb
34
+ Sequel::Seed.environment = :development
35
+ ```
36
+
37
+ Apply the seeds/fixtures
38
+
39
+ ```rb
40
+ DB = Sequel.connect(...)
41
+ Sequel::Seeder.apply(DB, "/path/to/seeds")
42
+ ```
43
+
44
+ ## Limitations
45
+
46
+ Only timestamped seeds files
47
+
48
+ ## License
49
+
50
+ [MIT License](http://earaujoassis.mit-license.org/) © Ewerton Assis
@@ -0,0 +1,202 @@
1
+ # Extension based upon Sequel::Migration and Sequel::Migrator
2
+ #
3
+ # Adds the Sequel::Seed and Sequel::Seeder classes, which allow
4
+ # the user to easily group entity changes and seed the database
5
+ # to a newer version only (migrations are directional, up or down;
6
+ # seeds are always applied).
7
+ #
8
+ # To load the extension:
9
+ #
10
+ # Sequel.extension :seed
11
+
12
+ module Sequel
13
+ class Seed
14
+ class << self
15
+ attr_accessor :environment
16
+ end
17
+
18
+ def self.apply
19
+ new.run
20
+ end
21
+
22
+ def self.descendants
23
+ @descendants ||= []
24
+ end
25
+
26
+ def self.inherited(base)
27
+ descendants << base
28
+ end
29
+
30
+ def run
31
+ end
32
+ end
33
+
34
+ def self.seed *env_labels, &block
35
+ return if env_labels.length > 0 && !env_labels.include?(Seed.environment)
36
+
37
+ seed = Class.new(Seed)
38
+ seed.class_eval(&block) if block_given?
39
+ Seed.inherited(seed) unless Seed.descendants.include? seed
40
+ seed
41
+ end
42
+
43
+ class Seeder
44
+ SEED_FILE_PATTERN = /\A(\d+)_.+\.rb\z/i.freeze
45
+ SEED_SPLITTER = '_'.freeze
46
+ MINIMUM_TIMESTAMP = 20000101
47
+
48
+ class Error < Sequel::Error
49
+ end
50
+
51
+ class NotCurrentError < Error
52
+ end
53
+
54
+ def self.apply(db, directory, opts = {})
55
+ seeder_class(directory).new(db, directory, opts).run
56
+ end
57
+
58
+ def self.seeder_class(directory)
59
+ if self.equal?(Seeder)
60
+ Dir.new(directory).each do |file|
61
+ next unless SEED_FILE_PATTERN.match(file)
62
+ return TimestampSeeder if file.split(SEED_SPLITTER, 2).first.to_i > MINIMUM_TIMESTAMP
63
+ end
64
+ raise(Error, 'seeder not available for files')
65
+ else
66
+ self
67
+ end
68
+ end
69
+
70
+ attr_reader :column
71
+
72
+ attr_reader :db
73
+
74
+ attr_reader :directory
75
+
76
+ attr_reader :ds
77
+
78
+ attr_reader :files
79
+
80
+ attr_reader :table
81
+
82
+ def initialize(db, directory, opts = {})
83
+ raise(Error, "Must supply a valid seed path") unless File.directory?(directory)
84
+ @db = db
85
+ @directory = directory
86
+ @allow_missing_seed_files = opts[:allow_missing_seed_files]
87
+ @files = get_seed_files
88
+ schema, table = @db.send(:schema_and_table, opts[:table] || self.class.const_get(:DEFAULT_SCHEMA_TABLE))
89
+ @table = schema ? Sequel::SQL::QualifiedIdentifier.new(schema, table) : table
90
+ @column = opts[:column] || self.class.const_get(:DEFAULT_SCHEMA_COLUMN)
91
+ @ds = schema_dataset
92
+ @use_transactions = opts[:use_transactions]
93
+ end
94
+
95
+ private
96
+
97
+ def checked_transaction(seed, &block)
98
+ use_trans = if @use_transactions.nil?
99
+ @db.supports_transactional_ddl?
100
+ else
101
+ @use_transactions
102
+ end
103
+
104
+ if use_trans
105
+ db.transaction(&block)
106
+ else
107
+ yield
108
+ end
109
+ end
110
+
111
+ def remove_seed_classes
112
+ Seed.descendants.each do |c|
113
+ Object.send(:remove_const, c.to_s) rescue nil
114
+ end
115
+ Seed.descendants.clear
116
+ end
117
+
118
+ def seed_version_from_file(filename)
119
+ filename.split(SEED_SPLITTER, 2).first.to_i
120
+ end
121
+ end
122
+
123
+ class TimestampSeeder < Seeder
124
+ DEFAULT_SCHEMA_COLUMN = :filename
125
+ DEFAULT_SCHEMA_TABLE = :schema_seeds
126
+
127
+ Error = Seeder::Error
128
+
129
+ attr_reader :applied_seeds
130
+
131
+ attr_reader :seed_tuples
132
+
133
+ def initialize(db, directory, opts = {})
134
+ super
135
+ @applied_seeds = get_applied_seeds
136
+ @seed_tuples = get_seed_tuples
137
+ end
138
+
139
+ def run
140
+ seed_tuples.each do |s, f|
141
+ t = Time.now
142
+ db.log_info("Begin applying seed #{f}")
143
+ checked_transaction(s) do
144
+ s.apply
145
+ fi = f.downcase
146
+ ds.insert(column => fi)
147
+ end
148
+ db.log_info("Finished applying seed #{f}, took #{sprintf('%0.6f', Time.now - t)} seconds")
149
+ end
150
+ nil
151
+ end
152
+
153
+ private
154
+
155
+ def get_applied_seeds
156
+ am = ds.select_order_map(column)
157
+ missing_seed_files = am - files.map{|f| File.basename(f).downcase}
158
+ if missing_seed_files.length > 0 && !@allow_missing_seed_files
159
+ raise(Error, "Applied seed files not in file system: #{missing_seed_files.join(', ')}")
160
+ end
161
+ am
162
+ end
163
+
164
+ def get_seed_files
165
+ files = []
166
+ Dir.new(directory).each do |file|
167
+ next unless SEED_FILE_PATTERN.match(file)
168
+ files << File.join(directory, file)
169
+ end
170
+ files.sort_by{|f| SEED_FILE_PATTERN.match(File.basename(f))[1].to_i}
171
+ end
172
+
173
+ def get_seed_tuples
174
+ remove_seed_classes
175
+ seeds = []
176
+ ms = Seed.descendants
177
+ files.each do |path|
178
+ f = File.basename(path)
179
+ fi = f.downcase
180
+ if !applied_seeds.include?(fi)
181
+ load(path)
182
+ el = [ms.last, f]
183
+ if ms.last.present? && !seeds.include?(el)
184
+ seeds << [ms.last, f]
185
+ end
186
+ end
187
+ end
188
+ seeds
189
+ end
190
+
191
+ def schema_dataset
192
+ c = column
193
+ ds = db.from(table)
194
+ if !db.table_exists?(table)
195
+ db.create_table(table){String c, :primary_key => true}
196
+ elsif !ds.columns.include?(c)
197
+ raise(Error, "Seeder table #{table} does not contain column #{c}")
198
+ end
199
+ ds
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ Sequel::Seed.environment = :test
4
+
5
+ describe Sequel.seed do
6
+ it 'should create a Seed descendant according to the current environment' do
7
+ seed = Sequel.seed(:test) do
8
+ end
9
+ expect(Sequel::Seed.descendants).to include seed
10
+ end
11
+
12
+ it 'should ignore a Seed not applicable to the current environment' do
13
+ seed = Sequel.seed(:development) do
14
+ end
15
+ expect(Sequel::Seed.descendants).not_to include seed
16
+ end
17
+
18
+ it 'should create a Seed applicable to every environment' do
19
+ seed = Sequel.seed do
20
+ end
21
+ expect(Sequel::Seed.descendants).to include seed
22
+ end
23
+ end
24
+
25
+ describe Sequel::Seeder do
26
+ let(:DB) {Sequel.sqlite}
27
+
28
+ context 'when there\'s no Seed created' do
29
+ it 'should not make any change to the database' do
30
+ expect {Sequel::Seeder.apply(DB, '/')}.not_to raise_error
31
+ expect(SpecModel.dataset.all.length).to be 0
32
+ end
33
+ end
34
+
35
+ context 'when there\'s a Seed created' do
36
+ it 'should change the database accordingly only once' do
37
+ Sequel.seed do
38
+ def run
39
+ SpecModel.create name: 'some name'
40
+ end
41
+ end
42
+
43
+ expect(Sequel::Seed.descendants.length).to be 1
44
+ expect {Sequel::Seeder.apply(DB, '/')}.not_to raise_error
45
+ expect(Sequel::Seed.descendants.length).to be 0
46
+ expect(SpecModel.dataset.all.length).to be 1
47
+ expect(SpecModel.dataset.first.name).to eq 'some name'
48
+ end
49
+ end
50
+
51
+ context 'when the specified Seed is not applicable to the current environment' do
52
+ let(:seed) {
53
+ Sequel.seed(:hithere) do
54
+ def run
55
+ SpecModel.create name: 'some name'
56
+ end
57
+ end
58
+ }
59
+
60
+ it 'should not make any change to the database' do
61
+ seed
62
+ expect(Sequel::Seed.descendants.length).to be 0
63
+ expect {Sequel::Seeder.apply(DB, '/')}.not_to raise_error
64
+ expect(SpecModel.dataset.all.length).to be 0
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,55 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup(:default, :development)
3
+
4
+ require 'sequel'
5
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/sequel/extensions/seed.rb')
6
+
7
+ RSpec.configure do |config|
8
+ Sequel.extension :seed
9
+
10
+ class Sequel::Seeder
11
+ def self.seeder_class(directory)
12
+ if self.equal?(Sequel::Seeder)
13
+ return Sequel::TimestampSeeder
14
+ else
15
+ self
16
+ end
17
+ end
18
+ end
19
+
20
+ class Sequel::TimestampSeeder
21
+ def run
22
+ seed_tuples.each {|s| s.apply}
23
+ Sequel::Seed.descendants.clear
24
+ end
25
+
26
+ private
27
+
28
+ def get_applied_seeds
29
+ []
30
+ end
31
+
32
+ def get_seed_files
33
+ []
34
+ end
35
+
36
+ def get_seed_tuples
37
+ Sequel::Seed.descendants
38
+ end
39
+ end
40
+
41
+ DB = Sequel.sqlite
42
+
43
+ DB.create_table(:spec_models) do
44
+ primary_key :id, :auto_increment => true
45
+ String :name
46
+ end
47
+
48
+ config.before(:each) do
49
+ SpecModel.dataset.delete
50
+ Sequel::Seed.descendants.clear
51
+ end
52
+ end
53
+
54
+ class SpecModel < Sequel::Model
55
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel-seed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Ewerton Assis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sequel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ description: A Sequel extension to make seeds/fixtures manageable like migrations
28
+ email: hello@dearaujoassis.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - CHANGELOG
34
+ - LICENSE
35
+ - README.md
36
+ - lib/sequel/extensions/seed.rb
37
+ - spec/extensions/seed_spec.rb
38
+ - spec/spec_helper.rb
39
+ homepage: https://github.com/earaujoassis/sequel-seed
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 1.9.1
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.4.8
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: A Sequel extension to make seeds/fixtures manageable like migrations
63
+ test_files: []