swift 0.14.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/API.rdoc +14 -14
  2. data/README.md +110 -61
  3. data/Rakefile +2 -5
  4. data/VERSION +1 -1
  5. data/lib/swift/adapter/mysql.rb +30 -0
  6. data/lib/swift/adapter/postgres.rb +27 -0
  7. data/lib/swift/adapter/sql.rb +23 -29
  8. data/lib/swift/adapter/sqlite3.rb +59 -0
  9. data/lib/swift/adapter.rb +129 -70
  10. data/lib/swift/attribute.rb +19 -8
  11. data/lib/swift/eventmachine.rb +49 -0
  12. data/lib/swift/identity_map.rb +7 -7
  13. data/lib/swift/migrations.rb +12 -12
  14. data/lib/swift/{scheme.rb → record.rb} +16 -17
  15. data/lib/swift/result.rb +24 -0
  16. data/lib/swift/statement.rb +25 -0
  17. data/lib/swift/synchrony.rb +38 -0
  18. data/lib/swift/validations.rb +2 -2
  19. data/lib/swift.rb +8 -6
  20. data/swift.gemspec +19 -31
  21. data/test/helper.rb +11 -6
  22. data/test/test_adapter.rb +11 -25
  23. data/test/test_async.rb +9 -12
  24. data/test/test_encoding.rb +2 -2
  25. data/test/test_error.rb +8 -8
  26. data/test/test_io.rb +2 -2
  27. data/test/{test_scheme.rb → test_record.rb} +6 -6
  28. data/test/test_swift.rb +9 -51
  29. data/test/test_timestamps.rb +1 -1
  30. data/test/test_transactions.rb +2 -2
  31. data/test/test_types.rb +3 -3
  32. data/test/test_validations.rb +2 -2
  33. metadata +20 -27
  34. data/ext/adapter.cc +0 -479
  35. data/ext/adapter.h +0 -13
  36. data/ext/adapter_io.cc +0 -62
  37. data/ext/adapter_io.h +0 -24
  38. data/ext/attribute.cc +0 -22
  39. data/ext/attribute.h +0 -8
  40. data/ext/datetime.cc +0 -96
  41. data/ext/datetime.h +0 -12
  42. data/ext/extconf.rb +0 -61
  43. data/ext/query.cc +0 -104
  44. data/ext/query.h +0 -20
  45. data/ext/result.cc +0 -229
  46. data/ext/result.h +0 -27
  47. data/ext/statement.cc +0 -116
  48. data/ext/statement.h +0 -22
  49. data/ext/swift.cc +0 -114
  50. data/ext/swift.h +0 -60
  51. data/lib/swift/db.rb +0 -89
data/lib/swift/adapter.rb CHANGED
@@ -1,3 +1,6 @@
1
+ require 'swift/result'
2
+ require 'swift/statement'
3
+
1
4
  module Swift
2
5
 
3
6
  # Adapter.
@@ -5,7 +8,11 @@ module Swift
5
8
  # @abstract
6
9
  # @see Swift::DB See Swift::DB for concrete adapters.
7
10
  class Adapter
8
- attr_reader :options
11
+ attr_reader :db
12
+
13
+ def initialize db
14
+ @db = db
15
+ end
9
16
 
10
17
  # Select by id(s).
11
18
  #
@@ -14,47 +21,46 @@ module Swift
14
21
  # @example Complex primary key.
15
22
  # Swift.db.get(UserAddress, user_id: 12, address_id: 15)
16
23
  #
17
- # @param [Swift::Scheme] scheme Concrete scheme subclass to load.
24
+ # @param [Swift::Record] record Concrete record subclass to load.
18
25
  # @param [Hash] keys Hash of id(s) <tt>{id_name: value}</tt>.
19
- # @return [Swift::Scheme, nil]
20
- # @see Swift::Scheme.get
26
+ # @return [Swift::Record, nil]
27
+ # @see Swift::Record.get
21
28
  #--
22
- # NOTE: Not significantly shorter than Scheme.db.first(User, 'id = ?', 12)
23
- def get scheme, keys
24
- resource = scheme.new(keys)
25
- prepare_get(scheme).execute(*resource.tuple.values_at(*scheme.header.keys)).first
29
+ # NOTE: Not significantly shorter than Record.db.first(User, 'id = ?', 12)
30
+ def get record, keys
31
+ resource = record.new(keys)
32
+ execute(record, command_get(record), *resource.tuple.values_at(*record.header.keys)).first
26
33
  end
27
34
 
28
35
  # Create one or more.
29
36
  #
30
- # @example Scheme.
37
+ # @example Record.
31
38
  # user = User.new(name: 'Apply Arthurton', age: 32)
32
39
  # Swift.db.create(User, user)
33
- # #=> Swift::Scheme
34
- # @example Coerce hash to scheme.
40
+ # #=> Swift::Record
41
+ # @example Coerce hash to record.
35
42
  # Swif.db.create(User, name: 'Apple Arthurton', age: 32)
36
- # #=> Swift::Scheme
43
+ # #=> Swift::Record
37
44
  # @example Multiple resources.
38
45
  # apple = User.new(name: 'Apple Arthurton', age: 32)
39
46
  # benny = User.new(name: 'Benny Arthurton', age: 30)
40
47
  # Swift.db.create(User, [apple, benny])
41
- # #=> Array<Swift::Scheme>
48
+ # #=> Array<Swift::Record>
42
49
  # @example Coerce multiple resources.
43
50
  # Swift.db.create(User, [{name: 'Apple Arthurton', age: 32}, {name: 'Benny Arthurton', age: 30}])
44
- # #=> Array<Swift::Scheme>
51
+ # #=> Array<Swift::Record>
45
52
  #
46
- # @param [Swift::Scheme] scheme Concrete scheme subclass to load.
47
- # @param [Swift::Scheme, Hash, Array<Swift::Scheme, Hash>] resources The resources to be saved.
48
- # @return [Swift::Scheme, Array<Swift::Scheme>]
49
- # @note Hashes will be coerced into a Swift::Scheme resource via Swift::Scheme#new
53
+ # @param [Swift::Record] record Concrete record subclass to load.
54
+ # @param [Swift::Record, Hash, Array<Swift::Record, Hash>] resources The resources to be saved.
55
+ # @return [Swift::Record, Array<Swift::Record>]
56
+ # @note Hashes will be coerced into a Swift::Record resource via Swift::Record#new
50
57
  # @note Passing a scalar will result in a scalar.
51
- # @see Swift::Scheme.create
52
- def create scheme, resources
53
- statement = prepare_create(scheme)
54
- result = [resources].flatten.map do |resource|
55
- resource = scheme.new(resource) unless resource.kind_of?(scheme)
56
- result = statement.execute(*resource.tuple.values_at(*scheme.header.insertable))
57
- resource.tuple[scheme.header.serial] = result.insert_id if scheme.header.serial
58
+ # @see Swift::Record.create
59
+ def create record, resources
60
+ result = [resources].flatten.map do |resource|
61
+ resource = record.new(resource) unless resource.kind_of?(record)
62
+ result = execute(command_create(record), *resource.tuple.values_at(*record.header.insertable))
63
+ resource.tuple[record.header.serial] = result.insert_id if record.header.serial
58
64
  resource
59
65
  end
60
66
  resources.kind_of?(Array) ? result : result.first
@@ -62,44 +68,43 @@ module Swift
62
68
 
63
69
  # Update one or more.
64
70
  #
65
- # @example Scheme.
71
+ # @example Record.
66
72
  # user = Swift.db.create(User, name: 'Apply Arthurton', age: 32)
67
73
  # user.name = 'Arthur Appleton'
68
74
  # Swift.db.update(User, user)
69
- # #=> Swift::Scheme
70
- # @example Coerce hash to scheme.
75
+ # #=> Swift::Record
76
+ # @example Coerce hash to record.
71
77
  # user = Swift.db.create(User, name: 'Apply Arthurton', age: 32)
72
78
  # user.name = 'Arthur Appleton'
73
79
  # Swif.db.update(User, user.tuple)
74
- # #=> Swift::Scheme
80
+ # #=> Swift::Record
75
81
  # @example Multiple resources.
76
82
  # apple = Swift.db.create(User, name: 'Apple Arthurton', age: 32)
77
83
  # benny = Swift.db.create(User, name: 'Benny Arthurton', age: 30)
78
84
  # Swift.db.update(User, [apple, benny])
79
- # #=> Array<Swift::Scheme>
85
+ # #=> Array<Swift::Record>
80
86
  # @example Coerce multiple resources.
81
87
  # apple = Swift.db.create(User, name: 'Apple Arthurton', age: 32)
82
88
  # benny = Swift.db.create(User, name: 'Benny Arthurton', age: 30)
83
89
  # Swift.db.update(User, [apple.tuple, benny.tuple])
84
- # #=> Array<Swift::Scheme>
90
+ # #=> Array<Swift::Record>
85
91
  #
86
- # @param [Swift::Scheme] scheme Concrete scheme subclass to load.
87
- # @param [Swift::Scheme, Hash, Array<Swift::Scheme, Hash>] resources The resources to be updated.
88
- # @return [Swift::Scheme, Swift::Result]
89
- # @note Hashes will be coerced into a Swift::Scheme resource via Swift::Scheme#new
92
+ # @param [Swift::Record] record Concrete record subclass to load.
93
+ # @param [Swift::Record, Hash, Array<Swift::Record, Hash>] resources The resources to be updated.
94
+ # @return [Swift::Record, Swift::Result]
95
+ # @note Hashes will be coerced into a Swift::Record resource via Swift::Record#new
90
96
  # @note Passing a scalar will result in a scalar.
91
- # @see Swift::Scheme#update
92
- def update scheme, resources
93
- statement = prepare_update(scheme)
94
- result = [resources].flatten.map do |resource|
95
- resource = scheme.new(resource) unless resource.kind_of?(scheme)
96
- keys = resource.tuple.values_at(*scheme.header.keys)
97
+ # @see Swift::Record#update
98
+ def update record, resources
99
+ result = [resources].flatten.map do |resource|
100
+ resource = record.new(resource) unless resource.kind_of?(record)
101
+ keys = resource.tuple.values_at(*record.header.keys)
97
102
 
98
103
  # TODO: Name the key field(s) missing.
99
- raise ArgumentError, "#{scheme} resource has incomplete key: #{resource.inspect}" \
104
+ raise ArgumentError, "#{record} resource has incomplete key: #{resource.inspect}" \
100
105
  unless keys.select(&:nil?).empty?
101
106
 
102
- statement.execute(*resource.tuple.values_at(*scheme.header.updatable), *keys)
107
+ execute(command_update(record), *resource.tuple.values_at(*record.header.updatable), *keys)
103
108
  resource
104
109
  end
105
110
  resources.kind_of?(Array) ? result : result.first
@@ -107,11 +112,11 @@ module Swift
107
112
 
108
113
  # Delete one or more.
109
114
  #
110
- # @example Scheme.
115
+ # @example Record.
111
116
  # user = Swift.db.create(User, name: 'Apply Arthurton', age: 32)
112
117
  # user.name = 'Arthur Appleton'
113
118
  # Swift.db.delete(User, user)
114
- # @example Coerce hash to scheme.
119
+ # @example Coerce hash to record.
115
120
  # user = Swift.db.create(User, name: 'Apply Arthurton', age: 32)
116
121
  # user.name = 'Arthur Appleton'
117
122
  # Swif.db.delete(User, user.tuple)
@@ -124,23 +129,22 @@ module Swift
124
129
  # benny = Swift.db.create(User, name: 'Benny Arthurton', age: 30)
125
130
  # Swift.db.delete(User, [apple.tuple, benny.tuple])
126
131
  #
127
- # @param [Swift::Scheme] scheme Concrete scheme subclass to load.
128
- # @param [Swift::Scheme, Hash, Array<Swift::Scheme, Hash>] resources The resources to be deleteed.
129
- # @return [Swift::Scheme, Array<Swift::Scheme>]
130
- # @note Hashes will be coerced into a Swift::Scheme resource via Swift::Scheme#new
132
+ # @param [Swift::Record] record Concrete record subclass to load.
133
+ # @param [Swift::Record, Hash, Array<Swift::Record, Hash>] resources The resources to be deleteed.
134
+ # @return [Swift::Record, Array<Swift::Record>]
135
+ # @note Hashes will be coerced into a Swift::Record resource via Swift::Record#new
131
136
  # @note Passing a scalar will result in a scalar.
132
- # @see Swift::Scheme#delete
133
- def delete scheme, resources
134
- statement = prepare_delete(scheme)
135
- result = [resources].flatten.map do |resource|
136
- resource = scheme.new(resource) unless resource.kind_of?(scheme)
137
- keys = resource.tuple.values_at(*scheme.header.keys)
137
+ # @see Swift::Record#delete
138
+ def delete record, resources
139
+ result = [resources].flatten.map do |resource|
140
+ resource = record.new(resource) unless resource.kind_of?(record)
141
+ keys = resource.tuple.values_at(*record.header.keys)
138
142
 
139
143
  # TODO: Name the key field(s) missing.
140
- raise ArgumentError, "#{scheme} resource has incomplete key: #{resource.inspect}" \
144
+ raise ArgumentError, "#{record} resource has incomplete key: #{resource.inspect}" \
141
145
  unless keys.select(&:nil?).empty?
142
146
 
143
- if result = statement.execute(*keys)
147
+ if result = execute(command_delete(record), *keys)
144
148
  resource.freeze
145
149
  end
146
150
  result
@@ -148,22 +152,77 @@ module Swift
148
152
  resources.kind_of?(Array) ? result : result.first
149
153
  end
150
154
 
151
- protected
152
- def prepare_get scheme
153
- raise NotImplementedError
154
- end
155
+ # Create a server side prepared statement
156
+ #
157
+ # @example
158
+ # finder = Swift.db.prepare(User, "select * from users where id > ?")
159
+ # user = finder.execute(1).first
160
+ # user.id
161
+ #
162
+ # @overload prepare(record, command)
163
+ # @param [Swift::Record] record Concrete record subclass to load.
164
+ # @param [String] command Command to be prepared by the underlying concrete adapter.
165
+ # @overload prepare(command)
166
+ # @param [String] command Command to be prepared by the underlying concrete adapter.
167
+ #
168
+ # @return [Swift::Statement, Swift::DB::Mysql::Statement, Swift::DB::Sqlite3::Statement, ...]
169
+ def prepare record = nil, command
170
+ record ? Statement.new(record, command) : db.prepare(command)
171
+ end
155
172
 
156
- def prepare_create scheme
157
- raise NotImplementedError
158
- end
173
+ # Trace commands being executed.
174
+ #
175
+ # @example
176
+ # Swift.db.trace { Swift.db.execute("select * from users") }
177
+ # @example
178
+ # Swift.db.trace(StringIO.new) { Swift.db.execute("select * from users") }
179
+ # @example
180
+ # Swift.db.trace(File.open('command.log', 'w')) { Swift.db.execute("select * from users") }
181
+ #
182
+ # @param [IO] io An optional IO object to log commands
183
+ # @return [Object] result Result from the block yielded to
184
+ def trace io = $stdout
185
+ @trace = io
186
+ result = yield
187
+ @trace = false
188
+ result
189
+ end
159
190
 
160
- def prepare_update scheme
161
- raise NotImplementedError
162
- end
191
+ # Check if the adapter commands are being traced.
192
+ #
193
+ # @return [TrueClass, FalseClass]
194
+ def trace?
195
+ !!@trace
196
+ end
163
197
 
164
- def prepare_delete scheme
165
- raise NotImplementedError
166
- end
198
+ # Execute a command using the underlying concrete adapter.
199
+ #
200
+ # @example
201
+ # Swift.db.execute("select * from users")
202
+ # @example
203
+ # Swift.db.execute(User, "select * from users where id = ?", 1)
204
+ #
205
+ # @overload execute(record, command, *bind)
206
+ # @param [Swift::Record] record Concrete record subclass to load.
207
+ # @param [String] command Command to be executed by the adapter.
208
+ # @param [*Object] bind Bind values.
209
+ # @overload execute(command, *bind)
210
+ # @param [String] command Command to be executed by the adapter.
211
+ # @param [*Object] bind Bind values.
212
+ #
213
+ # @return [Swift::Result, Swift::DB::Mysql::Result, Swift::DB::Sqlite3::Result, ...]
214
+ def execute command, *bind
215
+ start = Time.now
216
+ record, command = command, bind.shift if command.kind_of?(Class) && command < Record
217
+ record ? Result.new(record, db.execute(command, *bind)) : db.execute(command, *bind)
218
+ ensure
219
+ log_command(start, command, bind) if @trace
220
+ end
167
221
 
222
+ private
223
+
224
+ def log_command start, command, bind
225
+ @trace.print Time.now.strftime('%F %T.%N'), ' - ', (Time.now - start).to_f, ' - ', command, ' ', bind, $/
226
+ end
168
227
  end # Adapter
169
228
  end # Swift
@@ -7,10 +7,10 @@ module Swift
7
7
  attr_reader :name, :field, :key, :serial
8
8
 
9
9
  # @example
10
- # user = Class.new(Swift::Scheme)
10
+ # user = Class.new(Swift::Record)
11
11
  # Swift::Attribute.new(user, :name, Swift::Type::String)
12
12
  #
13
- # @param [Swift::Scheme] scheme
13
+ # @param [Swift::Record] record
14
14
  # @param [Symbol] name
15
15
  # @param [Hash] options
16
16
  # @option options [Object, Proc] :default
@@ -18,15 +18,26 @@ module Swift
18
18
  # @option options [TrueClass, FalseClass] :key
19
19
  # @option options [TrueClass, FalseClass] :serial
20
20
  #
21
- # @see Swift::Scheme
21
+ # @see Swift::Record
22
22
  # @see Swift::Type
23
- def initialize scheme, name, options = {}
23
+ def initialize record, name, options = {}
24
24
  @name = name
25
25
  @default = options.fetch(:default, nil)
26
26
  @field = options.fetch(:field, name)
27
27
  @key = options.fetch(:key, false)
28
28
  @serial = options.fetch(:serial, false)
29
- define_scheme_methods(scheme)
29
+ define_record_methods(record)
30
+ end
31
+
32
+ def default
33
+ case @default
34
+ when Numeric, Symbol, true, false, nil
35
+ @default
36
+ when Proc
37
+ @default.call
38
+ else
39
+ @default.dup
40
+ end
30
41
  end
31
42
 
32
43
  # The attributes field.
@@ -36,9 +47,9 @@ module Swift
36
47
  field.to_s
37
48
  end
38
49
 
39
- # Evals attribute accessors for this attribute into the scheme.
40
- def define_scheme_methods scheme
41
- scheme.class_eval <<-RUBY, __FILE__, __LINE__ + 1
50
+ # Evals attribute accessors for this attribute into the record.
51
+ def define_record_methods record
52
+ record.class_eval <<-RUBY, __FILE__, __LINE__ + 1
42
53
  def #{name}; tuple.fetch(:#{field}, nil) end
43
54
  def #{name}= value; tuple.store(:#{field}, value) end
44
55
  RUBY
@@ -0,0 +1,49 @@
1
+ require 'eventmachine'
2
+ require 'swift'
3
+
4
+ module Swift
5
+ # Eventmachine Adapter Extensions.
6
+ #
7
+ # This replaces the Adapter#execute method with a non-blocking asynchronous version.
8
+ class Adapter
9
+ alias :blocking_execute :execute
10
+
11
+ class EMHandler < EM::Connection
12
+ def initialize adapter, record, defer
13
+ @adapter = adapter
14
+ @record = record
15
+ @defer = defer
16
+ end
17
+
18
+ def notify_readable
19
+ detach
20
+ begin
21
+ @defer.succeed(@record ? Result.new(@record, @adapter.result) : @adapter.result)
22
+ rescue Exception => e
23
+ @defer.fail(e)
24
+ end
25
+ end
26
+ end
27
+
28
+ # Execute a command asynchronously.
29
+ #
30
+ # @example
31
+ # defer = Swift.db.execute(User, "select * from users where id = ?", 1)
32
+ # defer.callback do |user|
33
+ # p user.id
34
+ # end
35
+ # defer.errback do |error|
36
+ # p error
37
+ # end
38
+ #
39
+ # @see [Swift::Adapter]
40
+ def execute command, *bind
41
+ start = Time.now
42
+ record, command = command, bind.shift if command.kind_of?(Class) && command < Record
43
+ query(command, *bind)
44
+ EM::DefaultDeferrable.new.tap do |defer|
45
+ EM.watch(fileno, EMHandler, self, record, defer) {|c| c.notify_readable = true }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -33,17 +33,17 @@ module Swift
33
33
  end
34
34
  end
35
35
 
36
- class Scheme
36
+ class Record
37
37
  #--
38
38
  # TODO: Redefined method :(
39
39
  def self.load tuple
40
40
  im = [self, *tuple.values_at(*header.keys)]
41
- unless scheme = Swift.db.identity_map.get(im)
42
- scheme = allocate
43
- scheme.tuple = tuple
44
- Swift.db.identity_map.set(im, scheme)
41
+ unless record = Swift.db.identity_map.get(im)
42
+ record = allocate
43
+ record.tuple = tuple
44
+ Swift.db.identity_map.set(im, record)
45
45
  end
46
- scheme
46
+ record
47
47
  end
48
- end # Scheme
48
+ end # Record
49
49
  end # Swift
@@ -2,15 +2,15 @@ module Swift
2
2
  module Migrations
3
3
  module ClassMethods
4
4
  # @example
5
- # class User < Swift::Scheme
5
+ # class User < Swift::Record
6
6
  # migrations do |db|
7
7
  # db.execute %q{create table users(id serial, name text, age int)}
8
8
  # end
9
9
  # end
10
10
  #
11
- # @param [Proc] &migrations
11
+ # @param [Proc] migrations
12
12
  #
13
- # @see Swift::Scheme
13
+ # @see Swift::Record
14
14
  def migrations &migrations
15
15
  define_singleton_method(:migrate!, lambda{|db = Swift.db| migrations.call(db)})
16
16
  end
@@ -20,7 +20,7 @@ module Swift
20
20
  #
21
21
  # @param [Swift::Adapter] db
22
22
  #
23
- # @see Swift::Scheme
23
+ # @see Swift::Record
24
24
  def migrate! db = Swift.db
25
25
  db.migrate! self
26
26
  end
@@ -30,25 +30,25 @@ module Swift
30
30
  # @example
31
31
  # db.migrate! User
32
32
  #
33
- # @param [Swift::Scheme] scheme
33
+ # @param [Swift::Record] record
34
34
  #
35
35
  # @see Swift::Adapter::Sql
36
- def migrate! scheme
37
- keys = scheme.header.keys
38
- fields = scheme.header.map{|p| field_definition(p)}.join(', ')
36
+ def migrate! record
37
+ keys = record.header.keys
38
+ fields = record.header.map{|p| field_definition(p)}.join(', ')
39
39
  fields += ", primary key (#{keys.join(', ')})" unless keys.empty?
40
40
 
41
- execute("drop table if exists #{scheme.store} cascade")
42
- execute("create table #{scheme.store} (#{fields})")
41
+ execute("drop table if exists #{record.store} cascade")
42
+ execute("create table #{record.store} (#{fields})")
43
43
  end
44
44
  end # InstanceMethods
45
45
  end # Migrations
46
46
 
47
47
  def self.migrate! name = nil
48
- schema.each{|scheme| scheme.migrate!(db(name)) }
48
+ schema.each{|record| record.migrate!(db(name)) }
49
49
  end
50
50
 
51
- class Scheme
51
+ class Record
52
52
  extend Migrations::ClassMethods
53
53
  end
54
54
 
@@ -2,17 +2,17 @@ module Swift
2
2
 
3
3
  # A resource (instance) definition.
4
4
  #
5
- # @example A user scheme.
6
- # class User < Swift::Scheme
5
+ # @example A user record.
6
+ # class User < Swift::Record
7
7
  # store :users
8
8
  # attribute :id, Swift::Type::Integer, serial: true, key: true
9
9
  # attribute :name, Swift::Type::String
10
10
  # attribute :email, Swift::Type::String
11
11
  # attribute :updated_at, Swift::Type::Time
12
12
  # end # User
13
- class Scheme
13
+ class Record
14
14
  attr_accessor :tuple
15
- alias_method :scheme, :class
15
+ alias_method :record, :class
16
16
 
17
17
  # @example
18
18
  # User.new(
@@ -22,7 +22,7 @@ module Swift
22
22
  # )
23
23
  # @param [Hash] options Create resource and set attributes. <tt>{name: value}</tt>
24
24
  def initialize options = {}
25
- @tuple = scheme.header.new_tuple
25
+ @tuple = record.header.new_tuple
26
26
  options.each{|k, v| public_send(:"#{k}=", v)}
27
27
  end
28
28
 
@@ -37,7 +37,7 @@ module Swift
37
37
  # @param [Hash] options Update attributes. <tt>{name: value}</tt>
38
38
  def update options = {}
39
39
  options.each{|k, v| public_send(:"#{k}=", v)}
40
- Swift.db.update(scheme, self)
40
+ Swift.db.update(record, self)
41
41
  end
42
42
 
43
43
  # @example
@@ -48,7 +48,7 @@ module Swift
48
48
  # )
49
49
  # apple.delete
50
50
  def delete resources = self
51
- Swift.db.delete(scheme, resources)
51
+ Swift.db.delete(record, resources)
52
52
  end
53
53
 
54
54
  class << self
@@ -65,12 +65,12 @@ module Swift
65
65
  end
66
66
 
67
67
  def load tuple
68
- scheme = allocate
69
- scheme.tuple = tuple
70
- scheme
68
+ record = allocate
69
+ record.tuple = tuple
70
+ record
71
71
  end
72
72
 
73
- # Define a new attribute for this scheme.
73
+ # Define a new attribute for this record.
74
74
  #
75
75
  # @see Swift::Attribute#new
76
76
  def attribute name, type, options = {}
@@ -102,7 +102,7 @@ module Swift
102
102
  # updated_at: Time.now
103
103
  # )
104
104
  #
105
- # @param [Hash, Array<Hash>] options Create with attributes. <tt>{name: value}</tt>
105
+ # @param [Hash, Array<Hash>] resources Create with attributes. <tt>{name: value}</tt>
106
106
  def create resources = {}
107
107
  Swift.db.create(self, resources)
108
108
  end
@@ -115,7 +115,7 @@ module Swift
115
115
  # UserAddress.get(user_id: 12, address_id: 15)
116
116
  #
117
117
  # @param [Hash] keys Hash of id(s) <tt>{id_name: value}</tt>.
118
- # @return [Swift::Scheme, nil]
118
+ # @return [Swift::Record, nil]
119
119
  def get keys
120
120
  Swift.db.get(self, keys)
121
121
  end
@@ -143,10 +143,9 @@ module Swift
143
143
  # @param [*Object] binds Bind values.
144
144
  # @yield [Swift::Result]
145
145
  # @return [Swift::Result]
146
- def execute statement = '', *binds, &block
147
- Swift.db.execute(self, statement, *binds, &block)
146
+ def execute statement = '', *binds
147
+ Swift::Result.new(self, Swift.db.execute(statement, *binds))
148
148
  end
149
149
  end
150
- end # Scheme
150
+ end # Record
151
151
  end # Swift
152
-
@@ -0,0 +1,24 @@
1
+ require 'forwardable'
2
+
3
+ module Swift
4
+ # Result.
5
+ #
6
+ # Wrapper for command result. It lazily instantiates a new Swift::Record instance for each result row.
7
+ class Result
8
+ include Enumerable
9
+ extend Forwardable
10
+
11
+ def_delegators :@result, :selected_rows, :affected_rows, :fields, :types, :insert_id
12
+
13
+ def initialize record, result
14
+ @record = record
15
+ @result = result
16
+ end
17
+
18
+ def each
19
+ @result.each do |tuple|
20
+ yield @record.allocate.tap {|s| s.tuple = tuple}
21
+ end
22
+ end
23
+ end # Result
24
+ end # Swift
@@ -0,0 +1,25 @@
1
+ require 'swift/result'
2
+
3
+ module Swift
4
+ # Statement.
5
+ #
6
+ # Wrapper for server side prepared statements.
7
+ class Statement
8
+ def initialize record, command
9
+ @record = record
10
+ @statement = Swift.db.prepare(command)
11
+ end
12
+
13
+ # Execute a statement.
14
+ #
15
+ # @example
16
+ # statement = Swift.db.prepare(User, "select * from users where id > ?")
17
+ # statement.execute(10)
18
+ #
19
+ # @param [*Object] bind Bind values
20
+ # @return [Swift::Result]
21
+ def execute *bind, &block
22
+ Result.new(@record, @statement.execute(*bind), &block)
23
+ end
24
+ end # Statement
25
+ end # Swift
@@ -0,0 +1,38 @@
1
+ require 'em-synchrony'
2
+ require 'swift/eventmachine'
3
+
4
+ module Swift
5
+ # em-synchrony support for Swift::Adapter
6
+ #
7
+ # This replaces the default Adapter#execute with a version that uses EM::Synchrony.sync to wait for the
8
+ # defered command to complete. It assumes that the execute method is called inside a em-synchrony Fiber.
9
+ class Adapter
10
+ alias :aexecute :execute
11
+
12
+ # Execute a command asynchronously and pause the Fiber until the command finishes.
13
+ #
14
+ # @example
15
+ # EM.run do
16
+ # 3.times.each do |n|
17
+ # EM.synchrony do
18
+ # db = Swift.setup(:default, Swift::Adapter::Postgres, db: "swift_test")
19
+ # result = db.execute("select pg_sleep(3 - #{n}), #{n + 1} as qid")
20
+ #
21
+ # p result.first
22
+ # EM.stop if n == 0
23
+ # end
24
+ # end
25
+ # end
26
+ #
27
+ # @see [Swift::Adapter]
28
+ def execute *args
29
+ res = EM::Synchrony.sync aexecute(*args)
30
+ raise res if res.kind_of?(Error)
31
+ yield res if block_given?
32
+ res
33
+ rescue => e
34
+ $stderr.puts e, e.backtrace.join($/)
35
+ nil
36
+ end
37
+ end
38
+ end