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.
- data/API.rdoc +14 -14
- data/README.md +110 -61
- data/Rakefile +2 -5
- data/VERSION +1 -1
- data/lib/swift/adapter/mysql.rb +30 -0
- data/lib/swift/adapter/postgres.rb +27 -0
- data/lib/swift/adapter/sql.rb +23 -29
- data/lib/swift/adapter/sqlite3.rb +59 -0
- data/lib/swift/adapter.rb +129 -70
- data/lib/swift/attribute.rb +19 -8
- data/lib/swift/eventmachine.rb +49 -0
- data/lib/swift/identity_map.rb +7 -7
- data/lib/swift/migrations.rb +12 -12
- data/lib/swift/{scheme.rb → record.rb} +16 -17
- data/lib/swift/result.rb +24 -0
- data/lib/swift/statement.rb +25 -0
- data/lib/swift/synchrony.rb +38 -0
- data/lib/swift/validations.rb +2 -2
- data/lib/swift.rb +8 -6
- data/swift.gemspec +19 -31
- data/test/helper.rb +11 -6
- data/test/test_adapter.rb +11 -25
- data/test/test_async.rb +9 -12
- data/test/test_encoding.rb +2 -2
- data/test/test_error.rb +8 -8
- data/test/test_io.rb +2 -2
- data/test/{test_scheme.rb → test_record.rb} +6 -6
- data/test/test_swift.rb +9 -51
- data/test/test_timestamps.rb +1 -1
- data/test/test_transactions.rb +2 -2
- data/test/test_types.rb +3 -3
- data/test/test_validations.rb +2 -2
- metadata +20 -27
- data/ext/adapter.cc +0 -479
- data/ext/adapter.h +0 -13
- data/ext/adapter_io.cc +0 -62
- data/ext/adapter_io.h +0 -24
- data/ext/attribute.cc +0 -22
- data/ext/attribute.h +0 -8
- data/ext/datetime.cc +0 -96
- data/ext/datetime.h +0 -12
- data/ext/extconf.rb +0 -61
- data/ext/query.cc +0 -104
- data/ext/query.h +0 -20
- data/ext/result.cc +0 -229
- data/ext/result.h +0 -27
- data/ext/statement.cc +0 -116
- data/ext/statement.h +0 -22
- data/ext/swift.cc +0 -114
- data/ext/swift.h +0 -60
- 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 :
|
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::
|
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::
|
20
|
-
# @see Swift::
|
26
|
+
# @return [Swift::Record, nil]
|
27
|
+
# @see Swift::Record.get
|
21
28
|
#--
|
22
|
-
# NOTE: Not significantly shorter than
|
23
|
-
def get
|
24
|
-
resource =
|
25
|
-
|
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
|
37
|
+
# @example Record.
|
31
38
|
# user = User.new(name: 'Apply Arthurton', age: 32)
|
32
39
|
# Swift.db.create(User, user)
|
33
|
-
# #=> Swift::
|
34
|
-
# @example Coerce hash to
|
40
|
+
# #=> Swift::Record
|
41
|
+
# @example Coerce hash to record.
|
35
42
|
# Swif.db.create(User, name: 'Apple Arthurton', age: 32)
|
36
|
-
# #=> Swift::
|
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::
|
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::
|
51
|
+
# #=> Array<Swift::Record>
|
45
52
|
#
|
46
|
-
# @param [Swift::
|
47
|
-
# @param [Swift::
|
48
|
-
# @return [Swift::
|
49
|
-
# @note Hashes will be coerced into a Swift::
|
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::
|
52
|
-
def create
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
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::
|
70
|
-
# @example Coerce hash to
|
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::
|
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::
|
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::
|
90
|
+
# #=> Array<Swift::Record>
|
85
91
|
#
|
86
|
-
# @param [Swift::
|
87
|
-
# @param [Swift::
|
88
|
-
# @return [Swift::
|
89
|
-
# @note Hashes will be coerced into a Swift::
|
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::
|
92
|
-
def update
|
93
|
-
|
94
|
-
|
95
|
-
|
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, "#{
|
104
|
+
raise ArgumentError, "#{record} resource has incomplete key: #{resource.inspect}" \
|
100
105
|
unless keys.select(&:nil?).empty?
|
101
106
|
|
102
|
-
|
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
|
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
|
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::
|
128
|
-
# @param [Swift::
|
129
|
-
# @return [Swift::
|
130
|
-
# @note Hashes will be coerced into a Swift::
|
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::
|
133
|
-
def delete
|
134
|
-
|
135
|
-
|
136
|
-
|
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, "#{
|
144
|
+
raise ArgumentError, "#{record} resource has incomplete key: #{resource.inspect}" \
|
141
145
|
unless keys.select(&:nil?).empty?
|
142
146
|
|
143
|
-
if result =
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
191
|
+
# Check if the adapter commands are being traced.
|
192
|
+
#
|
193
|
+
# @return [TrueClass, FalseClass]
|
194
|
+
def trace?
|
195
|
+
!!@trace
|
196
|
+
end
|
163
197
|
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
data/lib/swift/attribute.rb
CHANGED
@@ -7,10 +7,10 @@ module Swift
|
|
7
7
|
attr_reader :name, :field, :key, :serial
|
8
8
|
|
9
9
|
# @example
|
10
|
-
# user = Class.new(Swift::
|
10
|
+
# user = Class.new(Swift::Record)
|
11
11
|
# Swift::Attribute.new(user, :name, Swift::Type::String)
|
12
12
|
#
|
13
|
-
# @param [Swift::
|
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::
|
21
|
+
# @see Swift::Record
|
22
22
|
# @see Swift::Type
|
23
|
-
def initialize
|
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
|
-
|
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
|
40
|
-
def
|
41
|
-
|
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
|
data/lib/swift/identity_map.rb
CHANGED
@@ -33,17 +33,17 @@ module Swift
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
class
|
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
|
42
|
-
|
43
|
-
|
44
|
-
Swift.db.identity_map.set(im,
|
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
|
-
|
46
|
+
record
|
47
47
|
end
|
48
|
-
end #
|
48
|
+
end # Record
|
49
49
|
end # Swift
|
data/lib/swift/migrations.rb
CHANGED
@@ -2,15 +2,15 @@ module Swift
|
|
2
2
|
module Migrations
|
3
3
|
module ClassMethods
|
4
4
|
# @example
|
5
|
-
# class User < Swift::
|
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]
|
11
|
+
# @param [Proc] migrations
|
12
12
|
#
|
13
|
-
# @see Swift::
|
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::
|
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::
|
33
|
+
# @param [Swift::Record] record
|
34
34
|
#
|
35
35
|
# @see Swift::Adapter::Sql
|
36
|
-
def migrate!
|
37
|
-
keys =
|
38
|
-
fields =
|
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 #{
|
42
|
-
execute("create table #{
|
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{|
|
48
|
+
schema.each{|record| record.migrate!(db(name)) }
|
49
49
|
end
|
50
50
|
|
51
|
-
class
|
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
|
6
|
-
# class User < Swift::
|
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
|
13
|
+
class Record
|
14
14
|
attr_accessor :tuple
|
15
|
-
alias_method :
|
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 =
|
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(
|
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(
|
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
|
-
|
69
|
-
|
70
|
-
|
68
|
+
record = allocate
|
69
|
+
record.tuple = tuple
|
70
|
+
record
|
71
71
|
end
|
72
72
|
|
73
|
-
# Define a new attribute for this
|
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>]
|
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::
|
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
|
147
|
-
Swift.db.execute(
|
146
|
+
def execute statement = '', *binds
|
147
|
+
Swift::Result.new(self, Swift.db.execute(statement, *binds))
|
148
148
|
end
|
149
149
|
end
|
150
|
-
end #
|
150
|
+
end # Record
|
151
151
|
end # Swift
|
152
|
-
|
data/lib/swift/result.rb
ADDED
@@ -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
|