swift 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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