alf-sequel 0.13.0

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.
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