swift 0.14.0 → 1.0.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 (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