swift 0.4.1

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Stateless Systems
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,236 @@
1
+ = Swift
2
+
3
+ * http://github.com/shanna/swift
4
+
5
+ == Description
6
+
7
+ A rational rudimentary object relational mapper.
8
+
9
+ == Dependencies
10
+
11
+ * ruby >= 1.9.1
12
+ * dbic++ >= 0.1.6
13
+ * mysql >= 5.0.17 or postgresql >= 8.4
14
+
15
+ dbic++ can be found here http://github.com/deepfryed/dbicpp
16
+
17
+ == Features
18
+
19
+ * Multiple databases.
20
+ * Prepared statements.
21
+ * Bind values.
22
+ * Transactions and named save points.
23
+ * EventMachine asynchronous interface.
24
+ * Migrations.
25
+
26
+ == Performance
27
+
28
+ Swift prefers performance when it doesn't compromise the Ruby-ish interface. It's unfair to compare Swift to DataMapper
29
+ and ActiveRecord which suffer under the weight of support for many more databases and legacy/alternative Ruby
30
+ implementations. That said obviously if Swift were slower it would be redundant so benchmark code does exist in
31
+ http://github.com/shanna/swift/tree/master/benchmarks
32
+
33
+ == Synopsis
34
+
35
+ === DB
36
+
37
+ require 'swift'
38
+
39
+ Swift.trace true # Debugging.
40
+ Swift.setup :default, Swift::DB::Postgres, db: 'swift'
41
+
42
+ # Block form db context.
43
+ Swift.db do |db|
44
+ db.execute('drop table if exists users')
45
+ db.execute('create table users(id serial, name text, email text)')
46
+
47
+ # Save points are supported.
48
+ db.transaction :named_save_point do
49
+ st = db.prepare('insert into users (name, email) values (?, ?)')
50
+ puts st.execute('Apple Arthurton', 'apple@arthurton.local').insert_id
51
+ puts st.execute('Benny Arthurton', 'benny@arthurton.local').insert_id
52
+ end
53
+
54
+ # Block result iteration.
55
+ db.prepare('select * from users').execute do |row|
56
+ puts row.inspect
57
+ end
58
+
59
+ # Enumerable.
60
+ result = db.prepare('select * from users where name like ?').execute('Benny%')
61
+ puts result.first
62
+ end
63
+
64
+ === DB Scheme Operations
65
+
66
+ Rudimentary object mapping. Provides a definition to the db methods for prepared (and cached) statements plus native
67
+ primitive Ruby type conversion.
68
+
69
+ require 'swift'
70
+
71
+ Swift.trace true # Debugging.
72
+ Swift.setup :default, Swift::DB::Postgres, db: 'swift'
73
+
74
+ class User < Swift::Scheme
75
+ store :users
76
+ attribute :id, Swift::Type::Integer, serial: true, key: true
77
+ attribute :name, Swift::Type::String
78
+ attribute :email, Swift::Type::String
79
+ attribute :updated_at, Swift::Type::Time
80
+ end # User
81
+
82
+ Swift.db do |db|
83
+ db.migrate! User
84
+
85
+ # Select Scheme instance (relation) instead of Hash.
86
+ users = db.prepare(User, 'select * from users limit 1').execute
87
+
88
+ # Make a change and update.
89
+ users.each{|user| user.updated_at = Time.now}
90
+ db.update(User, *users)
91
+
92
+ # Get a specific user by id.
93
+ user = db.get(User, id: 1)
94
+ puts user.name, user.email
95
+ end
96
+
97
+ === Scheme CRUD
98
+
99
+ Scheme/relation level helpers.
100
+
101
+ require 'swift'
102
+
103
+ Swift.trace true # Debugging.
104
+ Swift.setup :default, Swift::DB::Postgres, db: 'swift'
105
+
106
+ class User < Swift::Scheme
107
+ store :users
108
+ attribute :id, Swift::Type::Integer, serial: true, key: true
109
+ attribute :name, Swift::Type::String
110
+ attribute :email, Swift::Type::String
111
+ end # User
112
+
113
+ # Migrate it.
114
+ User.migrate!
115
+
116
+ # Create
117
+ User.create name: 'Apple Arthurton', email: 'apple@arthurton.local' # => User
118
+
119
+ # Get by key.
120
+ user = User.get id: 1
121
+
122
+ # Alter attribute and update in one.
123
+ user.update name: 'Jimmy Arthurton'
124
+
125
+ # Alter attributes and update.
126
+ user.name = 'Apple Arthurton'
127
+ user.update
128
+
129
+ # Destroy
130
+ user.destroy
131
+
132
+ === Conditions SQL syntax.
133
+
134
+ SQL is easy and most people know it so Swift ORM provides a simple symbol like syntax to convert resource
135
+ names to field names.
136
+
137
+ class User < Swift::Scheme
138
+ store :users
139
+ attribute :id, Swift::Type::Integer, serial: true, key: true
140
+ attribute :age, Swift::Type::Integer, field: 'ega'
141
+ attribute :name, Swift::Type::String, field: 'eman'
142
+ attribute :email, Swift::Type::String, field: 'liame'
143
+ end # User
144
+
145
+ # Convert :name and :age to fields.
146
+ # select * from users where eman like '%Arthurton' and ega > 20
147
+ users = User.all(':name like ? and :age > ?', '%Arthurton', 20)
148
+
149
+ === Identity Map
150
+
151
+ Swift comes with a simple identity map. Just require it after you load swift.
152
+
153
+ require 'swift'
154
+ require 'swift/identity_map'
155
+
156
+ class User < Swift::Scheme
157
+ store :users
158
+ attribute :id, Swift::Type::Integer, serial: true, key: true
159
+ attribute :age, Swift::Type::Integer, field: 'ega'
160
+ attribute :name, Swift::Type::String, field: 'eman'
161
+ attribute :email, Swift::Type::String, field: 'liame'
162
+ end # User
163
+
164
+ User.first(':name = ?', 'James Arthurton')
165
+ User.first(':name = ?', 'James Arthurton') # Gets same object reference
166
+
167
+
168
+ === Bulk inserts
169
+
170
+ Swift comes with adapter level support for bulk inserts for MySQL and PostgreSQL. This
171
+ is usually very fast (~5-10x faster) than regular prepared insert statements for larger
172
+ sets of data.
173
+
174
+ MySQL adapter - Overrides the MySQL C API and implements its own _infile_ handlers. This
175
+ means currently you *cannot* execute the following SQL using Swift
176
+
177
+ LOAD DATA LOCAL INFILE '/tmp/users.tab' INTO TABLE users;
178
+
179
+ But you can do it almost as fast in ruby,
180
+
181
+ require 'swift'
182
+
183
+ Swift.setup :default, Swift::DB::Mysql, db: 'swift'
184
+
185
+ # MySQL packet size is the usual limit, 8k is the packet size by default.
186
+ Swift.db do |db|
187
+ File.open('/tmp/users.tab') do |file|
188
+ count = db.write('users', %w{name email balance}, file)
189
+ end
190
+ end
191
+
192
+ You are not just limited to files - you can stream data from anywhere into MySQL and
193
+ PostgreSQL directly without creating temporary files.
194
+
195
+
196
+ == Benchmarks
197
+
198
+ The following bechmarks were run on a machine with 4G ram, 5200rpm sata drive,
199
+ Intel Core2Duo P8700 2.53GHz and stock PostgreSQL 8.4.1.
200
+
201
+ * 10,000 rows are created once.
202
+ * All the rows are selected once.
203
+ * All the rows are selected once and updated once.
204
+ * Memory footprint(rss) shows how much memory the benchmark used with GC disabled.
205
+ This gives an idea of total memory use and indirectly an idea of the number of
206
+ objects allocated and the pressure on Ruby GC if it were running. When GC is enabled,
207
+ the actual memory consumption might be much lower than the numbers below.
208
+
209
+ ./benchmarks/simple.rb -r 10000 -n 1
210
+
211
+ benchmark sys user total real rss
212
+ dm #create 0.390000 3.950000 4.340000 5.771812 244.32m
213
+ dm #select 0.150000 1.760000 1.910000 2.035583 128.97m
214
+ dm #update 0.690000 7.880000 8.570000 11.295239 603.30m
215
+
216
+ ar #create 0.930000 6.620000 7.550000 10.002911 367.82m
217
+ ar #select 0.050000 0.310000 0.360000 0.417127 38.82m
218
+ ar #update 0.770000 6.180000 6.950000 9.711788 361.93m
219
+
220
+ swift #create 0.180000 0.820000 1.000000 1.968757 27.35m
221
+ swift #select 0.010000 0.070000 0.080000 0.130234 9.85m
222
+ swift #update 0.250000 0.610000 0.860000 1.996165 29.35m
223
+ swift #write 0.000000 0.100000 0.100000 0.167199 6.23m
224
+
225
+
226
+ == TODO
227
+
228
+ * Tests.
229
+ * Assertions for dumb stuff. model < Model for methods in Adapter.
230
+ * Profile.
231
+
232
+ == Contributing
233
+
234
+ Go nuts! There is no style guide and I do not care if you write tests or comment code. If you write something neat just
235
+ send a pull request, tweet, email or yell it at me line by line in person.
236
+
@@ -0,0 +1,40 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.name = 'swift'
7
+ gem.summary = %q{A rational rudimentary database abstraction.}
8
+ gem.description = %q{A rational rudimentary database abstraction.}
9
+ gem.email = %w{shane.hanna@gmail.com deepfryed@gmail.com}
10
+ gem.homepage = 'http://github.com/shanna/swift'
11
+ gem.authors = ["Shane Hanna", "Bharanee 'Barney' Rathna"]
12
+ gem.extensions = FileList['ext/extconf.rb']
13
+ gem.files.reject!{|f| f =~ %r{\.gitignore|examples|benchmarks|memory/.*}}
14
+
15
+ gem.add_development_dependency 'minitest', '>= 1.7.0'
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ task :test => :check_dependencies
30
+ task :default => :test
31
+
32
+ require 'rake/rdoctask'
33
+ Rake::RDocTask.new do |rdoc|
34
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
35
+
36
+ rdoc.rdoc_dir = 'rdoc'
37
+ rdoc.title = "swift #{version}"
38
+ rdoc.rdoc_files.include('README*')
39
+ rdoc.rdoc_files.include('lib/**/*.rb')
40
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.1
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require_relative '../lib/swift'
4
+ require_relative '../lib/swift/pool'
5
+
6
+ adapter = ARGV.first =~ /mysql/i ? Swift::DB::Mysql : Swift::DB::Postgres
7
+ puts "Using DB: #{adapter}"
8
+
9
+ Swift.setup :default, adapter, db: 'swift'
10
+ Swift.trace true
11
+
12
+ # create test table
13
+ Swift.db do |db|
14
+ puts '-- create --'
15
+ db.execute('DROP TABLE IF EXISTS users')
16
+ db.execute('CREATE TABLE users(id serial, name text, email text)')
17
+
18
+ sample = DATA.read.split(/\n/).map {|v| v.split(/\t+/) }
19
+
20
+ puts '-- insert --'
21
+ ins = db.prepare('insert into users(name, email) values(?, ?)')
22
+ 10.times {|n| ins.execute(*sample[n%3]) }
23
+ end
24
+
25
+ puts '-- select 9 times with a pool of size 5 --'
26
+ Swift.trace false
27
+ Swift.pool(5) do |db|
28
+ (1..9).each do |n|
29
+ pause = '%0.3f' % ((20-n)/20.0)
30
+ pause = "case length(pg_sleep(#{pause})::text) when 0 then '#{pause}' else '' end as sleep"
31
+ db.execute("select #{pause}, * from users where id = ?", n) {|r| p r.first }
32
+ end
33
+ end
34
+ Swift.trace true
35
+
36
+ puts '-- multiple pools: size 2, size 1 --'
37
+ EM.run {
38
+ pool1 = Swift.pool(2)
39
+ pool2 = Swift.pool(1)
40
+
41
+ pool1.execute("select * from users limit 5 offset 0") do |rs|
42
+ puts '-- Inside pool1 #callback --'
43
+ rs.each {|r| p r }
44
+ pool1.execute("select * from users limit 5 offset 5") do |rs|
45
+ puts '-- Inside pool1 #callback again --'
46
+ rs.each {|r| p r }
47
+ EM.stop
48
+ end
49
+ end
50
+
51
+ pool2.execute("select * from users limit 5 offset 10") do |rs|
52
+ puts '-- Inside pool2 #callback --'
53
+ rs.each {|r| p r }
54
+ end
55
+ }
56
+
57
+ __END__
58
+ Apple Arthurton apple@example.com
59
+ Benny Arthurton benny@example.com
60
+ James Arthurton james@example.com
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/swift'
3
+ require 'pp'
4
+
5
+ class User < Swift::Scheme
6
+ store :users
7
+ attribute :id, Swift::Type::Integer, serial: true, key: true
8
+ attribute :name, Swift::Type::String
9
+ attribute :email, Swift::Type::String
10
+ end # User
11
+
12
+ adapter = ARGV.first =~ /mysql/i ? Swift::DB::Mysql : Swift::DB::Postgres
13
+ puts "Using DB: #{adapter}"
14
+
15
+ Swift.setup :default, adapter, db: 'swift'
16
+ Swift.trace true
17
+
18
+ Swift.db do |db|
19
+ db.migrate! User
20
+
21
+ puts '-- create --'
22
+ db.create(User,
23
+ {name: 'Apple Arthurton', email: 'apple@arthurton.local'},
24
+ {name: 'Benny Arthurton', email: 'benny@arthurton.local'}
25
+ )
26
+
27
+ puts '', '-- select --'
28
+ pp users = db.prepare(User, 'select * from users').execute.to_a
29
+
30
+ puts '', '-- update --'
31
+ db.update(User, *users.map!{|user| user.name = 'Fred Nurk'; user})
32
+ pp db.prepare(User, 'select * from users').execute.to_a
33
+
34
+ puts '', '-- get --'
35
+ pp db.get(User, id: 1)
36
+
37
+ puts '', '-- destroy --'
38
+ pp db.destroy(User, id: 1)
39
+ end
40
+
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/swift'
3
+ require 'pp'
4
+
5
+ class User < Swift::Scheme
6
+ store :users
7
+ attribute :id, Swift::Type::Integer, serial: true, key: true
8
+ attribute :name, Swift::Type::String
9
+ attribute :email, Swift::Type::String
10
+ attribute :active, Swift::Type::Boolean
11
+ attribute :created, Swift::Type::Time, default: proc { Time.now }
12
+ attribute :optional, Swift::Type::String, default: 'woot'
13
+ end # User
14
+
15
+ adapter = ARGV.first =~ /mysql/i ? Swift::DB::Mysql : Swift::DB::Postgres
16
+ puts "Using DB: #{adapter}"
17
+
18
+ Swift.setup :default, adapter, db: 'swift'
19
+ Swift.trace true
20
+
21
+ puts '-- migrate! --'
22
+ User.migrate!
23
+
24
+ puts '', '-- create --'
25
+ User.create name: 'Apple Arthurton', email: 'apple@arthurton.local'
26
+ User.create name: 'Benny Arthurton', email: 'benny@arthurton.local'
27
+
28
+ puts '', '-- all --'
29
+ pp User.all.to_a
30
+ # pp User.all(':name like ?', '%Arthurton').to_a
31
+
32
+ puts '', '-- first --'
33
+ pp User.first(':name like ?', '%Arthurton')
34
+
35
+ puts '', '-- get --'
36
+ pp user = User.get(id: 2)
37
+ pp user = User.get(id: 2)
38
+
39
+ puts '', '-- update --'
40
+ user.update(name: 'Jimmy Arthurton')
41
+
42
+ puts '', '-- destroy --'
43
+ user.destroy
44
+
45
+ puts '', '-- all --'
46
+ pp User.all.to_a