ethel-sequel 0.0.1

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.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ bin
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ethel-sequel.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'test-unit'
8
+ gem 'mocha'
9
+ gem 'guard-test'
10
+ gem 'rb-inotify', '~> 0.8.8'
11
+ gem 'sqlite3'
12
+ end
@@ -0,0 +1,8 @@
1
+ guard 'test' do
2
+ watch(%r{^lib/ethel/([^/]+/)*([^/]+)\.rb$}) do |m|
3
+ "test/unit/#{m[1]}test_#{m[2]}.rb"
4
+ end
5
+ watch(%r{^test/unit/([^/]+/)*test_.+\.rb$})
6
+ watch(%r{^test/integration/test_.+\.rb$})
7
+ watch('test/helper.rb') { 'test' }
8
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jeremy Stephens
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Ethel::Sequel
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'ethel-sequel'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install ethel-sequel
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+ task :default => :test
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ethel/sequel/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "ethel-sequel"
8
+ gem.version = Ethel::Sequel::VERSION
9
+ gem.authors = ["Jeremy Stephens"]
10
+ gem.email = ["jeremy.f.stephens@vanderbilt.edu"]
11
+ gem.description = %q{Adds Sequel source and target for Ethel}
12
+ gem.summary = %q{Sequel plugin for Ethel}
13
+ gem.homepage = "https://github.com/coupler/ethel-sequel"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'sequel'
21
+ gem.add_dependency 'ethel'
22
+ end
@@ -0,0 +1 @@
1
+ require 'ethel/sequel'
@@ -0,0 +1,6 @@
1
+ require 'ethel'
2
+ require 'sequel'
3
+
4
+ require 'ethel/sequel/version'
5
+ require 'ethel/sources/sequel'
6
+ require 'ethel/targets/sequel'
@@ -0,0 +1,5 @@
1
+ module Ethel
2
+ module Sequel
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ module Ethel
2
+ module Sources
3
+ class Sequel < Source
4
+ def initialize(*args)
5
+ if args.length == 1
6
+ @dataset = args[0]
7
+ @database = @dataset.db
8
+ else
9
+ @database = ::Sequel.connect(*args[1..-1])
10
+ @dataset = @database[args[0]]
11
+ @dataset_arg = args[0]
12
+ end
13
+ end
14
+
15
+ def schema
16
+ table =
17
+ if @dataset_arg.nil?
18
+ @dataset.first_source_table
19
+ else
20
+ @dataset_arg
21
+ end
22
+
23
+ @database.schema(table)
24
+ end
25
+
26
+ def each(&block)
27
+ @dataset.each(&block)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,66 @@
1
+ module Ethel
2
+ module Targets
3
+ class Sequel < Target
4
+ attr_accessor :force, :import_limit
5
+
6
+ def initialize(table_name, *args)
7
+ super
8
+
9
+ @database =
10
+ if args.length == 1 && args[0].kind_of?(::Sequel::Database)
11
+ args[0]
12
+ else
13
+ ::Sequel.connect(*args)
14
+ end
15
+
16
+ @table_name = table_name
17
+ @fields = []
18
+ @rows = []
19
+
20
+ @force = false
21
+ @import_limit = 10_000
22
+ end
23
+
24
+ def add_field(field)
25
+ @fields << field
26
+ end
27
+
28
+ def prepare
29
+ exists = @database.tables.include?(@table_name)
30
+ if exists && !@force
31
+ raise "Table #{@table_name} already exists"
32
+ else
33
+ generator = @database.create_table_generator
34
+ @fields.each do |field|
35
+ type =
36
+ case field.type
37
+ when :integer
38
+ Integer
39
+ when :string
40
+ String
41
+ end
42
+ generator.column(field.name, type)
43
+ end
44
+ if exists
45
+ @database.create_table!(@table_name, generator)
46
+ else
47
+ @database.create_table(@table_name, generator)
48
+ end
49
+ end
50
+ end
51
+
52
+ def add_row(row)
53
+ @rows << row
54
+
55
+ flush if @rows.length >= @import_limit
56
+ end
57
+
58
+ def flush
59
+ dataset = @database[@table_name]
60
+ keys = @fields.collect { |field| field.name.to_sym }
61
+ dataset.import(keys, @rows.collect { |r| r.values_at(*keys) })
62
+ @rows.clear
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ require 'test/unit'
12
+ require 'mocha/setup'
13
+ require 'tempfile'
14
+
15
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
16
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
17
+ require 'ethel/sequel'
18
+
19
+ class SequenceHelper
20
+ def initialize(name)
21
+ @seq = Mocha::Sequence.new(name)
22
+ end
23
+
24
+ def <<(expectation)
25
+ expectation.in_sequence(@seq)
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ require 'helper'
2
+
3
+ class TestMigration < Test::Unit::TestCase
4
+ test "basic migration" do
5
+ db = Sequel.sqlite
6
+ db.create_table(:foo) do
7
+ primary_key :id
8
+ String :bar
9
+ end
10
+ db[:foo].import([:bar], [%w{123}, %w{456}])
11
+ source = Ethel::Sources::Sequel.new(db[:foo])
12
+ target = Ethel::Targets::Sequel.new(:bar, db)
13
+ migration = Ethel::Migration.new(source, target)
14
+ migration.copy(source.fields[:id])
15
+ migration.cast(source.fields[:bar], :integer)
16
+ migration.run
17
+
18
+ assert_include db.tables, :bar
19
+ assert_equal([{:id => 1, :bar => 123}, {:id => 2, :bar => 456}],
20
+ db[:bar].all)
21
+ end
22
+ end
@@ -0,0 +1,83 @@
1
+ require 'helper'
2
+
3
+ module TestSources
4
+ class TestSequel < Test::Unit::TestCase
5
+ def self.const_missing(name)
6
+ if Ethel.const_defined?(name)
7
+ Ethel.const_get(name)
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def setup
14
+ @database = stub('database')
15
+ Sequel.stubs(:connect).returns(@database)
16
+ @dataset = stub('dataset', :db => @database)
17
+ @database.stubs(:[]).returns(@dataset)
18
+ end
19
+
20
+ test "subclass of Source" do
21
+ assert_equal Source, Sources::Sequel.superclass
22
+ end
23
+
24
+ test "initialize with table name, uri, and options" do
25
+ Sequel.expects(:connect).with('sqlite:/', :timeout => 1234).
26
+ returns(@database)
27
+ @database.expects(:[]).with(:foo).returns(@dataset)
28
+ assert_nothing_raised do
29
+ Sources::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234})
30
+ end
31
+ end
32
+
33
+ test "initialize with Sequel dataset" do
34
+ @dataset.expects(:db).returns(@database)
35
+ assert_nothing_raised { Sources::Sequel.new(@dataset) }
36
+ end
37
+
38
+ test "schema when initialized with Sequel dataset" do
39
+ source = Sources::Sequel.new(@dataset)
40
+
41
+ schema = [
42
+ [:id, {
43
+ :allow_null=>false, :default=>nil, :primary_key=>true,
44
+ :db_type=>"integer", :type=>:integer, :ruby_default=>nil
45
+ }],
46
+ [:foo, {
47
+ :allow_null=>true, :default=>nil, :primary_key=>false,
48
+ :db_type=>"varchar(255)", :type=>:string, :ruby_default=>nil
49
+ }]
50
+ ]
51
+ @dataset.expects(:first_source_table).returns(:foo)
52
+ @database.expects(:schema).with(:foo).returns(schema)
53
+ assert_equal schema, source.schema
54
+ end
55
+
56
+ test "schema when initialized with table_name, uri, and options" do
57
+ source = Sources::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234})
58
+
59
+ schema = [
60
+ [:id, {
61
+ :allow_null=>false, :default=>nil, :primary_key=>true,
62
+ :db_type=>"integer", :type=>:integer, :ruby_default=>nil
63
+ }],
64
+ [:foo, {
65
+ :allow_null=>true, :default=>nil, :primary_key=>false,
66
+ :db_type=>"varchar(255)", :type=>:string, :ruby_default=>nil
67
+ }]
68
+ ]
69
+ @database.expects(:schema).with(:foo).returns(schema)
70
+ assert_equal schema, source.schema
71
+ end
72
+
73
+ test "each" do
74
+ source = Sources::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234})
75
+
76
+ row = {:id => 1, :foo => 123}
77
+ @dataset.expects(:each).yields(row)
78
+ source.each do |actual_row|
79
+ assert_equal row, actual_row
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,127 @@
1
+ require 'helper'
2
+
3
+ module TestTargets
4
+ class TestSequel < Test::Unit::TestCase
5
+ def self.const_missing(name)
6
+ if Ethel.const_defined?(name)
7
+ Ethel.const_get(name)
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def setup
14
+ @database = stub('database')
15
+ ::Sequel.stubs(:connect).returns(@database)
16
+ @dataset = stub('dataset', :db => @database)
17
+ @database.stubs(:[]).returns(@dataset)
18
+ end
19
+
20
+ test "subclass of Target" do
21
+ assert_equal Target, Targets::Sequel.superclass
22
+ end
23
+
24
+ test "initialize with table name, uri, and options" do
25
+ ::Sequel.expects(:connect).with('sqlite:/', :timeout => 1234).
26
+ returns(@database)
27
+ assert_nothing_raised do
28
+ Targets::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234})
29
+ end
30
+ end
31
+
32
+ test "using Sequel::Database" do
33
+ ::Sequel.expects(:connect).never
34
+ @database.expects(:kind_of?).with(::Sequel::Database).returns(true)
35
+ Targets::Sequel.new(:foo, @database)
36
+ end
37
+
38
+ test "#prepare creates table if it doesn't exist" do
39
+ target = Targets::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234})
40
+
41
+ field_1 = stub('id field', :name => 'id', :type => :integer)
42
+ target.add_field(field_1)
43
+
44
+ field_2 = stub('foo field', :name => 'foo', :type => :string)
45
+ target.add_field(field_2)
46
+
47
+ seq = SequenceHelper.new('create table sequence')
48
+ generator = stub('table generator')
49
+ seq << @database.expects(:tables).returns([])
50
+ seq << @database.expects(:create_table_generator).returns(generator)
51
+ seq << generator.expects(:column).with('id', Integer)
52
+ seq << generator.expects(:column).with('foo', String)
53
+ seq << @database.expects(:create_table).with(:foo, generator)
54
+ target.prepare
55
+ end
56
+
57
+ test "#prepare raises error if table exists" do
58
+ target = Targets::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234})
59
+
60
+ field_1 = stub('id field', :name => 'id', :type => :integer)
61
+ target.add_field(field_1)
62
+
63
+ field_2 = stub('foo field', :name => 'foo', :type => :string)
64
+ target.add_field(field_2)
65
+
66
+ @database.expects(:tables).returns([:foo])
67
+ assert_raises { target.prepare }
68
+ end
69
+
70
+ test "#prepare forces table creation when force option is true" do
71
+ target = Targets::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234}, )
72
+ target.force = true
73
+
74
+ field_1 = stub('id field', :name => 'id', :type => :integer)
75
+ target.add_field(field_1)
76
+
77
+ field_2 = stub('foo field', :name => 'foo', :type => :string)
78
+ target.add_field(field_2)
79
+
80
+ seq = SequenceHelper.new('create table sequence')
81
+ generator = stub('table generator')
82
+ seq << @database.expects(:tables).returns([:foo])
83
+ seq << @database.expects(:create_table_generator).returns(generator)
84
+ seq << generator.expects(:column).with('id', Integer)
85
+ seq << generator.expects(:column).with('foo', String)
86
+ seq << @database.expects(:create_table!).with(:foo, generator)
87
+ target.prepare
88
+ end
89
+
90
+ test "#flush" do
91
+ target = Targets::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234})
92
+
93
+ field_1 = stub('id field', :name => 'id', :type => :integer)
94
+ target.add_field(field_1)
95
+
96
+ field_2 = stub('foo field', :name => 'foo', :type => :string)
97
+ target.add_field(field_2)
98
+
99
+ target.add_row({:id => 1, :foo => 'bar'})
100
+ target.add_row({:id => 2, :foo => 'baz'})
101
+ @database.expects(:[]).with(:foo).returns(@dataset)
102
+ @dataset.expects(:import).with([:id, :foo], [[1, 'bar'], [2, 'baz']])
103
+ target.flush
104
+ end
105
+
106
+ test "#add_row automatically flushes when limit reached" do
107
+ target = Targets::Sequel.new(:foo, 'sqlite:/', {:timeout => 1234})
108
+ target.import_limit = 2
109
+
110
+ field_1 = stub('id field', :name => 'id', :type => :integer)
111
+ target.add_field(field_1)
112
+
113
+ field_2 = stub('foo field', :name => 'foo', :type => :string)
114
+ target.add_field(field_2)
115
+
116
+ target.add_row({:id => 1, :foo => 'bar'})
117
+ @database.expects(:[]).with(:foo).returns(@dataset)
118
+ @dataset.expects(:import).with([:id, :foo], [[1, 'bar'], [2, 'baz']])
119
+ target.add_row({:id => 2, :foo => 'baz'})
120
+
121
+ target.add_row({:id => 3, :foo => 'test'})
122
+ @database.expects(:[]).with(:foo).returns(@dataset)
123
+ @dataset.expects(:import).with([:id, :foo], [[3, 'test'], [4, 'junk']])
124
+ target.add_row({:id => 4, :foo => 'junk'})
125
+ end
126
+ end
127
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ethel-sequel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeremy Stephens
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sequel
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: ethel
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Adds Sequel source and target for Ethel
47
+ email:
48
+ - jeremy.f.stephens@vanderbilt.edu
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - Guardfile
56
+ - LICENSE.txt
57
+ - README.md
58
+ - Rakefile
59
+ - ethel-sequel.gemspec
60
+ - lib/ethel-sequel.rb
61
+ - lib/ethel/sequel.rb
62
+ - lib/ethel/sequel/version.rb
63
+ - lib/ethel/sources/sequel.rb
64
+ - lib/ethel/targets/sequel.rb
65
+ - test/helper.rb
66
+ - test/integration/test_migration.rb
67
+ - test/unit/sources/test_sequel.rb
68
+ - test/unit/targets/test_sequel.rb
69
+ homepage: https://github.com/coupler/ethel-sequel
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.23
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Sequel plugin for Ethel
93
+ test_files:
94
+ - test/helper.rb
95
+ - test/integration/test_migration.rb
96
+ - test/unit/sources/test_sequel.rb
97
+ - test/unit/targets/test_sequel.rb