alf-sequel 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/CHANGELOG.md +4 -0
  2. data/Gemfile +19 -0
  3. data/Gemfile.lock +38 -0
  4. data/LICENCE.md +22 -0
  5. data/Manifest.txt +12 -0
  6. data/README.md +11 -0
  7. data/Rakefile +11 -0
  8. data/lib/alf/sequel/adapter.rb +57 -0
  9. data/lib/alf/sequel/cog.rb +92 -0
  10. data/lib/alf/sequel/compiler/predicate.rb +76 -0
  11. data/lib/alf/sequel/compiler.rb +168 -0
  12. data/lib/alf/sequel/connection/connection_methods.rb +46 -0
  13. data/lib/alf/sequel/connection/schema_methods.rb +76 -0
  14. data/lib/alf/sequel/connection/update_methods.rb +63 -0
  15. data/lib/alf/sequel/connection.rb +23 -0
  16. data/lib/alf/sequel/loader.rb +2 -0
  17. data/lib/alf/sequel/unit_of_work/atomic.rb +35 -0
  18. data/lib/alf/sequel/unit_of_work/delete.rb +24 -0
  19. data/lib/alf/sequel/unit_of_work/insert.rb +60 -0
  20. data/lib/alf/sequel/unit_of_work/update.rb +60 -0
  21. data/lib/alf/sequel/unit_of_work.rb +11 -0
  22. data/lib/alf/sequel/version.rb +16 -0
  23. data/lib/alf/sequel.rb +7 -0
  24. data/lib/alf-sequel.rb +1 -0
  25. data/spec/adapter/test_recognize.rb +50 -0
  26. data/spec/alf.db +0 -0
  27. data/spec/compiler/test_clip.rb +40 -0
  28. data/spec/compiler/test_compact.rb +18 -0
  29. data/spec/compiler/test_extend.rb +16 -0
  30. data/spec/compiler/test_intersect.rb +18 -0
  31. data/spec/compiler/test_join.rb +18 -0
  32. data/spec/compiler/test_leaf_operand.rb +24 -0
  33. data/spec/compiler/test_matching.rb +34 -0
  34. data/spec/compiler/test_not_matching.rb +34 -0
  35. data/spec/compiler/test_predicate.rb +141 -0
  36. data/spec/compiler/test_project.rb +64 -0
  37. data/spec/compiler/test_rename.rb +26 -0
  38. data/spec/compiler/test_restrict.rb +48 -0
  39. data/spec/compiler/test_sort.rb +18 -0
  40. data/spec/compiler/test_union.rb +18 -0
  41. data/spec/compiler_helper.rb +34 -0
  42. data/spec/connection/test_connection_uri.rb +54 -0
  43. data/spec/connection/test_delete.rb +20 -0
  44. data/spec/connection/test_heading.rb +16 -0
  45. data/spec/connection/test_insert.rb +24 -0
  46. data/spec/connection/test_keys.rb +24 -0
  47. data/spec/connection/test_lock.rb +15 -0
  48. data/spec/connection/test_ping.rb +26 -0
  49. data/spec/connection/test_relvar.rb +16 -0
  50. data/spec/connection/test_update.rb +20 -0
  51. data/spec/fixtures/sap.db +0 -0
  52. data/spec/fixtures/sap.rb +43 -0
  53. data/spec/spec_helper.rb +35 -0
  54. data/spec/test_assumptions.rb +14 -0
  55. data/spec/test_sequel.rb +10 -0
  56. data/spec/unit_of_work/atomic/test_run.rb +72 -0
  57. data/spec/unit_of_work/delete/test_delete.rb +39 -0
  58. data/spec/unit_of_work/insert/test_run.rb +71 -0
  59. data/spec/unit_of_work/update/test_run.rb +39 -0
  60. data/tasks/fixtures.rake +12 -0
  61. data/tasks/gem.rake +8 -0
  62. data/tasks/test.rake +6 -0
  63. metadata +174 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # 0.13.0 / 2013-07-29
2
+
3
+ The 0.13.0 version is a new birthday for alf. See `alf` main for CHANGELOG
4
+ history.
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'http://rubygems.org'
2
+
3
+ group :runtime do
4
+ #gem "alf-core", :git => "git://github.com/alf-tool/alf-core.git"
5
+ gem "alf-core", "~> 0.13.0"
6
+ gem "sequel", "~> 3.48"
7
+ end
8
+
9
+ group :test do
10
+ gem "rake", "~> 10.1"
11
+ gem "rspec", "~> 2.14"
12
+ gem "sqlite3", "~> 1.3", :platforms => ['mri', 'rbx']
13
+ gem "jdbc-sqlite3", "~> 3.7", :platforms => ['jruby']
14
+ end
15
+
16
+ group :development do
17
+ gem "rake", "~> 10.1"
18
+ gem "rspec", "~> 2.14"
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ alf-core (0.13.0)
5
+ domain (~> 1.0)
6
+ myrrha (~> 3.0)
7
+ path (~> 1.3)
8
+ sexpr (~> 0.5.1)
9
+ diff-lcs (1.2.4)
10
+ domain (1.0.0)
11
+ jdbc-sqlite3 (3.7.2.1)
12
+ myrrha (3.0.0)
13
+ domain (~> 1.0)
14
+ path (1.3.1)
15
+ rake (10.1.0)
16
+ rspec (2.14.1)
17
+ rspec-core (~> 2.14.0)
18
+ rspec-expectations (~> 2.14.0)
19
+ rspec-mocks (~> 2.14.0)
20
+ rspec-core (2.14.4)
21
+ rspec-expectations (2.14.0)
22
+ diff-lcs (>= 1.1.3, < 2.0)
23
+ rspec-mocks (2.14.1)
24
+ sequel (3.48.0)
25
+ sexpr (0.5.1)
26
+ sqlite3 (1.3.7)
27
+
28
+ PLATFORMS
29
+ java
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ alf-core (~> 0.13.0)
34
+ jdbc-sqlite3 (~> 3.7)
35
+ rake (~> 10.1)
36
+ rspec (~> 2.14)
37
+ sequel (~> 3.48)
38
+ sqlite3 (~> 1.3)
data/LICENCE.md ADDED
@@ -0,0 +1,22 @@
1
+ # The MIT Licence
2
+
3
+ Copyright (c) 2012 - Bernard Lambeau
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.
data/Manifest.txt ADDED
@@ -0,0 +1,12 @@
1
+ sequel.gemspec
2
+ sequel.noespec
3
+ CHANGELOG.md
4
+ Gemfile
5
+ Gemfile.lock
6
+ lib/**/*
7
+ LICENCE.md
8
+ Manifest.txt
9
+ Rakefile
10
+ README.md
11
+ spec/**/*
12
+ tasks/**/*
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Alf::Sequel
2
+
3
+ [![Build Status](https://secure.travis-ci.org/alf-tool/alf-sequel.png)](http://travis-ci.org/alf-tool/alf-sequel)
4
+ [![Dependency Status](https://gemnasium.com/alf-tool/alf-sequel.png)](https://gemnasium.com/alf-tool/alf-sequel)
5
+
6
+ A sequel adapter for alf
7
+
8
+ ## Links
9
+
10
+ http://github.com/alf-tool/alf
11
+ http://github.com/alf-tool/alf-sequel
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # We run tests by default
2
+ task :default => :test
3
+
4
+ #
5
+ # Install all tasks found in tasks folder
6
+ #
7
+ # See .rake files there for complete documentation.
8
+ #
9
+ Dir["tasks/*.rake"].each do |taskfile|
10
+ load taskfile
11
+ end
@@ -0,0 +1,57 @@
1
+ module Alf
2
+ module Sequel
3
+ class Adapter < Alf::Adapter
4
+
5
+ class << self
6
+
7
+ def recognizes?(conn_spec)
8
+ case conn_spec
9
+ when ::Sequel::Database
10
+ true
11
+ when String
12
+ require 'uri'
13
+ uri = URI::parse(conn_spec) rescue nil
14
+ (uri && uri.scheme) or looks_a_sqlite_file?(conn_spec)
15
+ when Hash
16
+ conn_spec = Tools.symbolize_keys(conn_spec)
17
+ conn_spec[:adapter] && conn_spec[:database]
18
+ else
19
+ looks_a_sqlite_file?(conn_spec)
20
+ end
21
+ end
22
+
23
+ # Returns true if `f` looks like a sqlite file
24
+ def looks_a_sqlite_file?(f)
25
+ return false unless Path.like?(f)
26
+ path = Path(f)
27
+ path.parent.directory? and ['db', 'sqlite', 'sqlite3'].include?(path.ext)
28
+ end
29
+
30
+ def sqlite_protocol
31
+ defined?(JRUBY_VERSION) ? "jdbc:sqlite" : "sqlite"
32
+ end
33
+
34
+ def sequel_db(conn_spec)
35
+ require 'sequel' unless defined?(::Sequel)
36
+ case conn_spec
37
+ when ::Sequel::Database
38
+ conn_spec
39
+ when String, Path
40
+ conn_spec = "#{sqlite_protocol}://#{conn_spec}" if looks_a_sqlite_file?(conn_spec)
41
+ ::Sequel.connect(conn_spec)
42
+ else
43
+ raise ArgumentError, "Unrecognized connection specification `#{conn_spec.inspect}`"
44
+ end
45
+ end
46
+
47
+ end # class << self
48
+
49
+ # Returns a low-level connection on this adapter
50
+ def connection
51
+ Connection.new(conn_spec)
52
+ end
53
+
54
+ ::Alf::Adapter.register(:sequel, self)
55
+ end # class Adapter
56
+ end # module Sequel
57
+ end # module Alf
@@ -0,0 +1,92 @@
1
+ module Alf
2
+ module Sequel
3
+ class Cog
4
+ include Engine::Cog
5
+
6
+ def initialize(connection, opts)
7
+ @connection = connection
8
+ @opts = opts
9
+ end
10
+ attr_reader :connection, :opts
11
+
12
+ def as; opts[:as]; end
13
+ def dataset; opts[:dataset]; end
14
+
15
+ ### Cog
16
+
17
+ def to_relation
18
+ Relation.coerce(to_a)
19
+ end
20
+
21
+ def each
22
+ dataset.each(&Proc.new)
23
+ end
24
+
25
+ ### Delegation to Dataset, that is, facade over ::Sequel itself
26
+
27
+ def select(attrs)
28
+ branch dataset: dataset.select(*qualify(attrs))
29
+ end
30
+
31
+ def rename(attrs, opts)
32
+ branch dataset: dataset.select(*qualify(attrs)).from_self(opts),
33
+ as: opts[:alias]
34
+ end
35
+
36
+ def distinct(*args, &bl)
37
+ branch dataset: dataset.distinct(*args, &bl)
38
+ end
39
+
40
+ def order(*args, &bl)
41
+ branch dataset: dataset.order(*args, &bl)
42
+ end
43
+
44
+ def filter(*args, &bl)
45
+ branch dataset: dataset.filter(*args, &bl)
46
+ end
47
+
48
+ def intersect(other, opts={})
49
+ branch dataset: dataset.intersect(other.dataset, opts),
50
+ as: opts[:alias]
51
+ end
52
+
53
+ def join(other, cols, opts={})
54
+ join = dataset.inner_join(other.dataset, cols, :table_alias => opts[:alias])
55
+ branch dataset: join.from_self(opts),
56
+ as: opts[:alias]
57
+ end
58
+
59
+ def union(other, opts={})
60
+ branch dataset: dataset.union(other.dataset, opts),
61
+ as: opts[:alias]
62
+ end
63
+
64
+ ### compilation tools
65
+
66
+ def sql
67
+ dataset.sql
68
+ end
69
+
70
+ def qualify(attributes)
71
+ return attributes unless as
72
+ case attributes
73
+ when Symbol
74
+ ::Sequel.qualify(as, attributes)
75
+ when Hash
76
+ attributes.map{|k,v| ::Sequel.as(::Sequel.qualify(as, k), v) }
77
+ else
78
+ attributes.map{|a| ::Sequel.qualify(as, a)}
79
+ end
80
+ end
81
+
82
+ def branch(opts = {})
83
+ Cog.new connection, self.opts.merge(opts)
84
+ end
85
+
86
+ def to_s
87
+ "Alf::Sequel::Cog|#{sql}"
88
+ end
89
+
90
+ end # class Operand
91
+ end # module Sequel
92
+ end # module Alf
@@ -0,0 +1,76 @@
1
+ module Alf
2
+ module Sequel
3
+ class Compiler
4
+ class Predicate < Sexpr::Processor
5
+
6
+ def call(predicate)
7
+ super(predicate.expr)
8
+ end
9
+
10
+ def on_missing(sexpr)
11
+ throw :pass
12
+ end
13
+ alias :on_native :on_missing
14
+
15
+ def on_tautology(sexpr)
16
+ ::Sequel::SQL::BooleanConstant.new(true)
17
+ end
18
+
19
+ def on_contradiction(sexpr)
20
+ ::Sequel::SQL::BooleanConstant.new(false)
21
+ end
22
+
23
+ def on_var_ref(sexpr)
24
+ if qualifier = options[:qualifier]
25
+ ::Sequel.qualify(qualifier, sexpr.last)
26
+ else
27
+ ::Sequel.expr(sexpr.last)
28
+ end
29
+ end
30
+
31
+ def on_literal(sexpr)
32
+ sexpr.last.nil? ? nil : ::Sequel.expr(sexpr.last)
33
+ end
34
+
35
+ def on_eq(sexpr)
36
+ left, right = apply(sexpr.left), apply(sexpr.right)
37
+ ::Sequel.expr(left => right)
38
+ end
39
+
40
+ def on_neq(sexpr)
41
+ left, right = apply(sexpr.left), apply(sexpr.right)
42
+ ~::Sequel.expr(left => right)
43
+ end
44
+
45
+ def on_dyadic_comp(sexpr)
46
+ left, right = apply(sexpr.left), apply(sexpr.right)
47
+ left.send(sexpr.operator_symbol, right)
48
+ end
49
+ alias :on_lt :on_dyadic_comp
50
+ alias :on_lte :on_dyadic_comp
51
+ alias :on_gt :on_dyadic_comp
52
+ alias :on_gte :on_dyadic_comp
53
+
54
+ def on_in(sexpr)
55
+ left = apply(sexpr.var_ref)
56
+ ::Sequel.expr(left => sexpr.values)
57
+ end
58
+
59
+ def on_not(sexpr)
60
+ ~apply(sexpr.last)
61
+ end
62
+
63
+ def on_and(sexpr)
64
+ body = sexpr.sexpr_body
65
+ body[1..-1].inject(apply(body.first)){|f,t| f & apply(t) }
66
+ end
67
+
68
+ def on_or(sexpr)
69
+ body = sexpr.sexpr_body
70
+ body[1..-1].inject(apply(body.first)){|f,t| f | apply(t) }
71
+ end
72
+
73
+ end # class Predicate
74
+ end # class Compiler
75
+ end # module Sequel
76
+ end # module Alf
@@ -0,0 +1,168 @@
1
+ module Alf
2
+ module Sequel
3
+ class Compiler < Algebra::Compiler
4
+
5
+ def pass(expr)
6
+ rewrite(expr)
7
+ end
8
+ alias :on_missing :pass
9
+
10
+ def next_alias
11
+ @as ||= 0
12
+ :"t#{@as += 1}"
13
+ end
14
+
15
+ def on_leaf_operand(expr)
16
+ if Algebra::Operand::Named===expr
17
+ expr.connection.cog(expr.name, :alias => next_alias)
18
+ else
19
+ expr.to_cog
20
+ end
21
+ end
22
+
23
+ ### non relational
24
+
25
+ alias :on_autonum :pass
26
+ alias :on_coerce :pass
27
+ alias :on_defaults :pass
28
+ alias :on_generator :pass
29
+
30
+ def on_clip(expr)
31
+ rewrite(expr){|rw|
32
+ rw.operand.select(expr.stay_attributes)
33
+ }
34
+ end
35
+
36
+ def on_compact(expr)
37
+ rewrite(expr){|rw|
38
+ rw.operand.distinct
39
+ }
40
+ end
41
+
42
+ def on_sort(expr)
43
+ rewrite(expr){|rw|
44
+ operand = rw.operand
45
+ ordering = expr.ordering.to_a.map{|(col,dir)|
46
+ ::Sequel.send(dir, operand.qualify(col))
47
+ }
48
+ operand.order(*ordering)
49
+ }
50
+ end
51
+
52
+ ### relational
53
+
54
+ alias :on_extend :pass
55
+ alias :on_group :pass
56
+ alias :on_infer_heading :pass
57
+
58
+ def on_intersect(expr)
59
+ rewrite(expr){|rw|
60
+ rw.left.intersect(rw.right, :alias => next_alias)
61
+ }
62
+ end
63
+
64
+ def on_join(expr)
65
+ rewrite(expr){|rw|
66
+ rw.left.join(rw.right, expr.common_attributes.to_a, :alias => next_alias)
67
+ }
68
+ end
69
+
70
+ def on_matching(expr)
71
+ rewrite(expr) do |rw|
72
+ rw.left.filter(matching2filter(expr, rw))
73
+ end
74
+ end
75
+
76
+ def on_not_matching(expr)
77
+ rewrite(expr) do |rw|
78
+ rw.left.filter(~matching2filter(expr, rw))
79
+ end
80
+ end
81
+
82
+ def matching2filter(expr, rw)
83
+ commons = expr.common_attributes.to_a
84
+ if commons.size==1
85
+ # (NOT) IN (SELECT ...)
86
+ pred = ::Alf::Predicate.in(commons.first, rw.right.select(commons).dataset)
87
+ Predicate.new(:qualifier => rw.left.as).call(pred)
88
+ elsif commons.size==0
89
+ # (NOT) EXISTS (SELECT ... no join condition ...)
90
+ rw.right.dataset.exists
91
+ else
92
+ # (NOT) EXISTS (SELECT ...)
93
+ filter = Hash[rw.left.qualify(commons).zip(rw.right.qualify(commons))]
94
+ filter = ::Sequel.expr filter
95
+ filter = rw.right.filter(filter)
96
+ filter.dataset.exists
97
+ end
98
+ end
99
+
100
+ alias :on_minus :pass
101
+
102
+ def on_project(expr)
103
+ rewrite(expr){|rw|
104
+ compiled = rw.operand.select(expr.stay_attributes)
105
+ compiled = compiled.distinct unless key_preserving?(expr){ false }
106
+ compiled
107
+ }
108
+ end
109
+
110
+ alias :on_quota :pass
111
+ alias :on_rank :pass
112
+
113
+ def on_rename(expr)
114
+ rewrite(expr){|rw|
115
+ rw.operand.rename(expr.complete_renaming.to_hash, :alias => next_alias)
116
+ }
117
+ end
118
+
119
+ def on_restrict(expr)
120
+ rewrite(expr){|rw|
121
+ filter = Predicate.new(:qualifier => rw.operand.as).call(rw.predicate)
122
+ rw.operand.filter(filter)
123
+ }
124
+ end
125
+
126
+ alias :on_summarize :pass
127
+ alias :on_ungroup :pass
128
+
129
+ def on_union(expr)
130
+ rewrite(expr){|rw|
131
+ rw.left.union(rw.right, :alias => next_alias)
132
+ }
133
+ end
134
+
135
+ alias :on_unwrap :pass
136
+ alias :on_wrap :pass
137
+
138
+ private
139
+
140
+ def key_preserving?(expr)
141
+ expr.key_preserving?
142
+ rescue NotSupportedError
143
+ block_given? ? yield : raise
144
+ end
145
+
146
+ private
147
+
148
+ def rewrite(expr)
149
+ rewrited = copy_and_apply(expr)
150
+ if block_given? and rewrited.operands.all?{|op| recognized?(op) }
151
+ catch(:pass){ rewrited = yield(rewrited) }
152
+ end
153
+ rewrited = engine.call(rewrited) unless recognized?(rewrited)
154
+ rewrited
155
+ end
156
+
157
+ def recognized?(op)
158
+ op.is_a?(Cog)
159
+ end
160
+
161
+ def engine
162
+ @engine ||= Engine::Compiler.new
163
+ end
164
+
165
+ end # class Compiler
166
+ end # module Sequel
167
+ end # module Alf
168
+ require_relative 'compiler/predicate'
@@ -0,0 +1,46 @@
1
+ module Alf
2
+ module Sequel
3
+ class Connection
4
+ module ConnectionMethods
5
+
6
+ def connection_uri(with_password = false)
7
+ if conn_spec.is_a?(Hash)
8
+ cs = Tuple(self.conn_spec)
9
+ adapter, host, port, database, user, password = cs.values_at(:adapter, :host, :port, :database, :user, :password)
10
+ user = "#{user}:#{password}" if user and password and with_password
11
+ user = "#{user}@" if user
12
+ host = "localhost" if host.nil? and not(adapter =~ /sqlite/)
13
+ host = "#{host}:#{port}" if host and port
14
+ host = "/#{user}#{host}" if host or user
15
+ "#{adapter}:/#{host}/#{database}"
16
+ elsif conn_spec.is_a?(String)
17
+ conn_spec
18
+ else
19
+ "Alf::Sequel::Connection(#{conn_spec})"
20
+ end
21
+ end
22
+
23
+ def ping
24
+ sequel_db.test_connection
25
+ end
26
+
27
+ def close
28
+ @sequel_db.disconnect if @sequel_db && @sequel_db != conn_spec
29
+ end
30
+
31
+ def with_sequel_db
32
+ yield(sequel_db)
33
+ end
34
+
35
+ private
36
+
37
+ # Yields a Sequel::Database object
38
+ def sequel_db
39
+ @sequel_db ||= Adapter.sequel_db(conn_spec)
40
+ block_given? ? yield(@sequel_db) : @sequel_db
41
+ end
42
+
43
+ end # module ConnectionMethods
44
+ end # module Connection
45
+ end # module Sequel
46
+ end # module Alf
@@ -0,0 +1,76 @@
1
+ module Alf
2
+ module Sequel
3
+ class Connection
4
+ module SchemaMethods
5
+
6
+ def knows?(name)
7
+ sequel_db.table_exists?(name)
8
+ end
9
+
10
+ def dataset(name)
11
+ sequel_db[name]
12
+ end
13
+
14
+ def cog(name, opts={})
15
+ if as = opts[:alias]
16
+ Cog.new(self, dataset: dataset(:"#{name}___#{as}"), as: as)
17
+ else
18
+ Cog.new(self, dataset: dataset(name))
19
+ end
20
+ end
21
+
22
+ def heading(name)
23
+ h = {}
24
+ sequel_db.schema(name).each do |pair|
25
+ column_name, info = pair
26
+ h[column_name] = dbtype_to_ruby_type(info)
27
+ end
28
+ Heading.new(h)
29
+ end
30
+
31
+ def keys(name)
32
+ # take the indexes
33
+ indexes = sequel_db.indexes(name).
34
+ values.
35
+ select{|i| i[:unique] == true }.
36
+ map{|i| AttrList.coerce(i[:columns]) }.
37
+ sort{|a1, a2| a1.size <=> a2.size}
38
+
39
+ # take single keys as well
40
+ key = sequel_db.schema(name).
41
+ select{|(colname, colinfo)| colinfo[:primary_key] }.
42
+ map(&:first)
43
+ indexes.unshift(AttrList.coerce(key)) unless key.empty?
44
+
45
+ Keys.new(indexes)
46
+ end
47
+
48
+ def migrate!(opts)
49
+ unless f = opts.migrations_folder
50
+ raise Alf::ConfigError, "Migrations folder not set"
51
+ end
52
+ with_sequel_db do |db|
53
+ ::Sequel.extension(:migration)
54
+ ::Sequel::Migrator.apply(db, f)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def dbtype_to_ruby_type(info)
61
+ return Object unless info[:type]
62
+ begin
63
+ Kernel.eval(info[:type].to_s.capitalize)
64
+ rescue NameError
65
+ case info[:type]
66
+ when :datetime then DateTime
67
+ when :boolean then Alf::Boolean
68
+ else Object
69
+ end
70
+ end
71
+ end
72
+
73
+ end # module SchemaMethods
74
+ end # module Connection
75
+ end # module Sequel
76
+ end # module Alf
@@ -0,0 +1,63 @@
1
+ module Alf
2
+ module Sequel
3
+ class Connection
4
+ module UpdateMethods
5
+
6
+ # Yields the block in a transaction
7
+ def in_transaction(&bl)
8
+ sequel_db.transaction(&bl)
9
+ end
10
+
11
+ def lock(name, mode, &bl)
12
+ with_dataset(name){|ds|
13
+ if ds.respond_to?(:lock)
14
+ ds.lock(mode.to_s.upcase, &bl)
15
+ else
16
+ yield
17
+ end
18
+ }
19
+ end
20
+
21
+ # Inserts `tuples` in the relvar called `name`
22
+ def insert(name, tuples)
23
+ insert_uow(name, tuples).run
24
+ end
25
+
26
+ # Delete from the relvar called `name`
27
+ def delete(name, predicate)
28
+ delete_uow(name, predicate).run
29
+ end
30
+
31
+ # Updates the relvar called `name`
32
+ def update(name, updating, predicate)
33
+ update_uow(name, updating, predicate).run
34
+ end
35
+
36
+ public # should be private
37
+
38
+ def with_dataset(name, predicate = nil)
39
+ ds = name.is_a?(Symbol) ? sequel_db[name] : name
40
+ if predicate && !predicate.tautology?
41
+ ds = ds.filter(Compiler::Predicate.call(predicate))
42
+ end
43
+ yield(ds) if block_given?
44
+ end
45
+
46
+ private
47
+
48
+ def insert_uow(name, tuples)
49
+ UnitOfWork::Insert.new(self, name, tuples)
50
+ end
51
+
52
+ def delete_uow(name, predicate)
53
+ UnitOfWork::Delete.new(self, name, predicate)
54
+ end
55
+
56
+ def update_uow(name, updating, predicate)
57
+ UnitOfWork::Update.new(self, name, updating, predicate)
58
+ end
59
+
60
+ end # module UpdateMethods
61
+ end # module Connection
62
+ end # module Sequel
63
+ end # module Alf