ethel-sequel 0.0.1

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