motion-sqlite3 0.3.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rvmrc +4 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +74 -0
- data/Guardfile +9 -0
- data/README.md +56 -0
- data/Rakefile +21 -0
- data/app/app_delegate.rb +5 -0
- data/lib/motion-sqlite3.rb +16 -0
- data/lib/motion-sqlite3/database.rb +62 -0
- data/lib/motion-sqlite3/error.rb +4 -0
- data/lib/motion-sqlite3/result_set.rb +57 -0
- data/lib/motion-sqlite3/statement.rb +86 -0
- data/lib/motion-sqlite3/version.rb +3 -0
- data/motion-sqlite3.gemspec +19 -0
- data/spec/database_spec.rb +66 -0
- data/vendor/sqlite3/dummy.m +0 -0
- data/vendor/sqlite3/sqlite3.bridgesupport +2049 -0
- data/vendor/sqlite3/sqlite3.h +7029 -0
- metadata +78 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 60363684d12e16e0ccb74acc26918d1548972762
|
|
4
|
+
data.tar.gz: b79313617ce0179fc585320b48d22d6d765a34c9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: cc32f386588abeaf391a715d1f65deb5e359ed50f3fac8eebbc8be52ddc7bd4a17704f31c8c6279086d956392e847efcaa3d08bc75c23783906b354f2006b6e0
|
|
7
|
+
data.tar.gz: 0c1aed2f2d261d4f72a2b1397df1aeec5a54c30fd4973f2d7768ff355c57c9bd8d9fbc3b2a4782bd09c1977bb209f8b505ccd38fe9a8361c3a98a820dfcbcec4
|
data/.gitignore
ADDED
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
GEM
|
|
2
|
+
remote: https://rubygems.org/
|
|
3
|
+
specs:
|
|
4
|
+
activesupport (3.2.11)
|
|
5
|
+
i18n (~> 0.6)
|
|
6
|
+
multi_json (~> 1.0)
|
|
7
|
+
addressable (2.3.2)
|
|
8
|
+
cocoapods (0.16.1)
|
|
9
|
+
activesupport (~> 3.2.6)
|
|
10
|
+
colored (~> 1.2)
|
|
11
|
+
escape (~> 0.0.4)
|
|
12
|
+
faraday (~> 0.8.1)
|
|
13
|
+
json (~> 1.7.3)
|
|
14
|
+
octokit (~> 1.7)
|
|
15
|
+
open4 (~> 1.3.0)
|
|
16
|
+
rake (~> 0.9.4)
|
|
17
|
+
xcodeproj (~> 0.4.1)
|
|
18
|
+
coderay (1.0.8)
|
|
19
|
+
colored (1.2)
|
|
20
|
+
escape (0.0.4)
|
|
21
|
+
faraday (0.8.4)
|
|
22
|
+
multipart-post (~> 1.1)
|
|
23
|
+
faraday_middleware (0.9.0)
|
|
24
|
+
faraday (>= 0.7.4, < 0.9)
|
|
25
|
+
guard (1.6.2)
|
|
26
|
+
listen (>= 0.6.0)
|
|
27
|
+
lumberjack (>= 1.0.2)
|
|
28
|
+
pry (>= 0.9.10)
|
|
29
|
+
terminal-table (>= 1.4.3)
|
|
30
|
+
thor (>= 0.14.6)
|
|
31
|
+
guard-motion (0.1.1)
|
|
32
|
+
guard (>= 1.1.0)
|
|
33
|
+
rake (>= 0.9)
|
|
34
|
+
hashie (1.2.0)
|
|
35
|
+
i18n (0.6.1)
|
|
36
|
+
json (1.7.6)
|
|
37
|
+
listen (0.7.2)
|
|
38
|
+
lumberjack (1.0.2)
|
|
39
|
+
method_source (0.8.1)
|
|
40
|
+
motion-cocoapods (1.2.1)
|
|
41
|
+
cocoapods (>= 0.14.0)
|
|
42
|
+
multi_json (1.5.0)
|
|
43
|
+
multipart-post (1.1.5)
|
|
44
|
+
netrc (0.7.7)
|
|
45
|
+
octokit (1.22.0)
|
|
46
|
+
addressable (~> 2.2)
|
|
47
|
+
faraday (~> 0.8)
|
|
48
|
+
faraday_middleware (~> 0.9)
|
|
49
|
+
hashie (~> 1.2)
|
|
50
|
+
multi_json (~> 1.3)
|
|
51
|
+
netrc (~> 0.7.7)
|
|
52
|
+
open4 (1.3.0)
|
|
53
|
+
pry (0.9.11.4)
|
|
54
|
+
coderay (~> 1.0.5)
|
|
55
|
+
method_source (~> 0.8)
|
|
56
|
+
slop (~> 3.4)
|
|
57
|
+
rake (0.9.6)
|
|
58
|
+
rb-fsevent (0.9.3)
|
|
59
|
+
slop (3.4.3)
|
|
60
|
+
terminal-table (1.4.5)
|
|
61
|
+
thor (0.17.0)
|
|
62
|
+
xcodeproj (0.4.2)
|
|
63
|
+
activesupport (~> 3.2.6)
|
|
64
|
+
colored (~> 1.2)
|
|
65
|
+
|
|
66
|
+
PLATFORMS
|
|
67
|
+
ruby
|
|
68
|
+
|
|
69
|
+
DEPENDENCIES
|
|
70
|
+
cocoapods
|
|
71
|
+
guard-motion
|
|
72
|
+
motion-cocoapods
|
|
73
|
+
rake (~> 0.9.0)
|
|
74
|
+
rb-fsevent (~> 0.9.1)
|
data/Guardfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Introduction
|
|
2
|
+
------------
|
|
3
|
+
This is a tiny wrapper around the SQLite C API that's written in RubyMotion. It is intentionally bare so as to limit the surface area of code that interacts with the raw C API (misuse of the C API can cause resource leaks and seg faults). Think of it more as a driver for SQLite than anything else.
|
|
4
|
+
|
|
5
|
+
Whenever possible, it uses Ruby idioms like blocks and exceptions.
|
|
6
|
+
|
|
7
|
+
Sample code
|
|
8
|
+
-----------
|
|
9
|
+
|
|
10
|
+
#### DB in memory
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
db = SQLite3::Database.new(":memory:")
|
|
14
|
+
db.execute("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)")
|
|
15
|
+
db.execute("INSERT INTO test (name, age) VALUES (?, ?)", ["brad", 28])
|
|
16
|
+
db.execute("INSERT INTO test (name, age) VALUES (?, ?)", ["sparky", 24])
|
|
17
|
+
|
|
18
|
+
rows = []
|
|
19
|
+
db.execute("SELECT * FROM test") do |row|
|
|
20
|
+
rows << row
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
rows.should == [
|
|
24
|
+
{ id: 1, name: "brad", age: 28 },
|
|
25
|
+
{ id: 2, name: "sparky", age: 24 }
|
|
26
|
+
]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
#### DB in your resources folder
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
# File is stored in your /project/resources/ folder
|
|
33
|
+
db = SQLite3::Database.new(File.join(App.resources_path, "my_db.sqlite"))
|
|
34
|
+
|
|
35
|
+
rows = []
|
|
36
|
+
db.execute("SELECT * FROM test ORDER BY id") do |row|
|
|
37
|
+
rows << row
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
*this code assumes you're using BubbleWrap for access to `App.resources_path`.
|
|
42
|
+
|
|
43
|
+
Usage
|
|
44
|
+
----------
|
|
45
|
+
* Pass a filename to the constructor, or ":memory:". The database is held open for the lifetime of the object.
|
|
46
|
+
* Use `execute` to run SQL statements. All SQL statements are first prepared, and parameters can be passed as an Array or a Hash. If a Hash is passed, then the SQLite named parameter syntax is assumed to be in use.
|
|
47
|
+
* Use `execute_debug` to see the SQL statement and paramaters passed in the REPL. You should not use this method in production.
|
|
48
|
+
* Use `execute_scalar` to run SQL statements and return the first column of the first row. This is useful for queries like `SELECT COUNT(*) FROM posts`.
|
|
49
|
+
|
|
50
|
+
Caveats
|
|
51
|
+
-------
|
|
52
|
+
* The way it links with libsqlite3.dylib is hacky: a blank .m file in the vendor folder. Will need to fix this.
|
|
53
|
+
|
|
54
|
+
Status
|
|
55
|
+
----------
|
|
56
|
+
Still in early stages. I'm hacking on it.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
$:.unshift("/Library/RubyMotion/lib")
|
|
3
|
+
require 'motion/project/template/ios'
|
|
4
|
+
|
|
5
|
+
require "bundler/gem_tasks"
|
|
6
|
+
require "bundler/setup"
|
|
7
|
+
Bundler.require :default
|
|
8
|
+
|
|
9
|
+
Motion::Project::App.setup do |app|
|
|
10
|
+
app.name = 'motion-sqlite3'
|
|
11
|
+
|
|
12
|
+
base_dir = File.dirname(__FILE__)
|
|
13
|
+
app.files += Dir.glob(File.join(base_dir, "lib/motion-sqlite3/**/*.rb"))
|
|
14
|
+
|
|
15
|
+
if ENV["DEFAULT_PROVISIONING_PROFILE"]
|
|
16
|
+
app.provisioning_profile = ENV["DEFAULT_PROVISIONING_PROFILE"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
app.libs << "/usr/lib/libsqlite3.dylib"
|
|
20
|
+
app.vendor_project('vendor/sqlite3', :static, :products => [])
|
|
21
|
+
end
|
data/app/app_delegate.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'motion-sqlite3/version'
|
|
2
|
+
|
|
3
|
+
unless defined?(Motion::Project::Config)
|
|
4
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Motion::Project::App.setup do |app|
|
|
8
|
+
Dir.glob(File.join(File.dirname(__FILE__), "motion-sqlite3/**/*.rb")).each do |file|
|
|
9
|
+
app.files.unshift(file)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
app.libs << "/usr/lib/libsqlite3.dylib"
|
|
13
|
+
|
|
14
|
+
vendor_dir = File.expand_path(File.join(File.dirname(__FILE__), "../vendor/sqlite3"))
|
|
15
|
+
app.vendor_project(vendor_dir, :static, :products => [])
|
|
16
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module SQLite3
|
|
2
|
+
class Database
|
|
3
|
+
def initialize(filename)
|
|
4
|
+
@handle = Pointer.new(::Sqlite3.type)
|
|
5
|
+
|
|
6
|
+
result = sqlite3_open(filename, @handle)
|
|
7
|
+
raise SQLite3Error, sqlite3_errmsg(@handle.value) if result != SQLITE_OK
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def execute(sql, params = nil, &block)
|
|
11
|
+
raise ArgumentError if sql.nil?
|
|
12
|
+
|
|
13
|
+
#puts "*** #{sql}"
|
|
14
|
+
#puts " #{params.inspect}" if params
|
|
15
|
+
|
|
16
|
+
prepare(sql, params) do |statement|
|
|
17
|
+
results = statement.execute
|
|
18
|
+
|
|
19
|
+
if block_given?
|
|
20
|
+
results.each do |result|
|
|
21
|
+
yield result
|
|
22
|
+
end
|
|
23
|
+
else
|
|
24
|
+
rows = []
|
|
25
|
+
|
|
26
|
+
results.each do |result|
|
|
27
|
+
rows << result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
rows
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def execute_debug(*args, &block)
|
|
36
|
+
puts "*** #{args[0]}"
|
|
37
|
+
puts " #{args[1].inspect}" if args[1]
|
|
38
|
+
|
|
39
|
+
execute(*args, &block)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def execute_scalar(*args)
|
|
43
|
+
execute(*args).first.values.first
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def prepare(sql, params, &block)
|
|
49
|
+
statement = Statement.new(@handle, sql, params)
|
|
50
|
+
result = nil
|
|
51
|
+
|
|
52
|
+
begin
|
|
53
|
+
result = yield statement
|
|
54
|
+
|
|
55
|
+
ensure
|
|
56
|
+
statement.finalize
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
result
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module SQLite3
|
|
2
|
+
class ResultSet
|
|
3
|
+
def initialize(statement, handle)
|
|
4
|
+
@statement = statement
|
|
5
|
+
@handle = handle
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def each(&block)
|
|
9
|
+
until @statement.done?
|
|
10
|
+
yield current_row
|
|
11
|
+
|
|
12
|
+
@statement.step
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def columns
|
|
19
|
+
@result_columns ||= begin
|
|
20
|
+
columns = {}
|
|
21
|
+
|
|
22
|
+
count = sqlite3_column_count(@handle.value)
|
|
23
|
+
0.upto(count-1) do |i|
|
|
24
|
+
name = sqlite3_column_name(@handle.value, i).to_sym
|
|
25
|
+
type = sqlite3_column_type(@handle.value, i)
|
|
26
|
+
|
|
27
|
+
columns[name] = ColumnMetadata.new(i, type)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
columns
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def current_row
|
|
35
|
+
row = {}
|
|
36
|
+
|
|
37
|
+
columns.each do |name, metadata|
|
|
38
|
+
case metadata.type
|
|
39
|
+
when SQLITE_NULL
|
|
40
|
+
row[name] = nil
|
|
41
|
+
when SQLITE_TEXT
|
|
42
|
+
row[name] = sqlite3_column_text(@handle.value, metadata.index)
|
|
43
|
+
when SQLITE_BLOB
|
|
44
|
+
row[name] = NSData.dataWithBytes(sqlite3_column_blob(@handle.value, metadata.index), length: sqlite3_column_bytes(@handle.value, metadata.index))
|
|
45
|
+
when SQLITE_INTEGER
|
|
46
|
+
row[name] = sqlite3_column_int(@handle.value, metadata.index)
|
|
47
|
+
when SQLITE_FLOAT
|
|
48
|
+
row[name] = sqlite3_column_double(@handle.value, metadata.index)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
row
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class ColumnMetadata < Struct.new(:index, :type); end
|
|
57
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module SQLite3
|
|
2
|
+
class Statement
|
|
3
|
+
def initialize(db, sql, params)
|
|
4
|
+
@db = db
|
|
5
|
+
@handle = Pointer.new(::Sqlite3_stmt.type)
|
|
6
|
+
@result = nil
|
|
7
|
+
|
|
8
|
+
prepare(sql)
|
|
9
|
+
bind(params)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def done?
|
|
13
|
+
@result == SQLITE_DONE
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def execute
|
|
17
|
+
step
|
|
18
|
+
|
|
19
|
+
ResultSet.new(self, @handle)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def finalize
|
|
23
|
+
sqlite3_finalize(@handle.value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def step
|
|
27
|
+
@result = sqlite3_step(@handle.value)
|
|
28
|
+
unless @result == SQLITE_DONE || @result == SQLITE_ROW
|
|
29
|
+
raise SQLite3Error, sqlite3_errmsg(@db.value)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def bind(params)
|
|
36
|
+
case params
|
|
37
|
+
when Hash
|
|
38
|
+
params.each { |name, value| bind_parameter(name, value) }
|
|
39
|
+
when Array
|
|
40
|
+
params.each_with_index { |value, i| bind_parameter(i+1, value) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def bind_parameter(name, value)
|
|
45
|
+
index = column_index(name)
|
|
46
|
+
|
|
47
|
+
case value
|
|
48
|
+
when NilClass
|
|
49
|
+
result = sqlite3_bind_null(@handle.value, index)
|
|
50
|
+
when String
|
|
51
|
+
result = sqlite3_bind_text(@handle.value, index, value, -1, lambda { |arg| })
|
|
52
|
+
when Integer
|
|
53
|
+
result = sqlite3_bind_int(@handle.value, index, value)
|
|
54
|
+
when Float
|
|
55
|
+
result = sqlite3_bind_double(@handle.value, index, value)
|
|
56
|
+
when NSData
|
|
57
|
+
result = sqlite3_bind_blob(@handle.value, index, value.bytes, value.length, lambda { |arg| })
|
|
58
|
+
else
|
|
59
|
+
raise SQLite3Error, "unable to bind #{value} to #{name}: unexpected type #{value.class}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
raise SQLite3Error, sqlite3_errmsg(@db.value) if result != SQLITE_OK
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def column_index(name)
|
|
66
|
+
index = 0
|
|
67
|
+
|
|
68
|
+
case name
|
|
69
|
+
when String, Symbol
|
|
70
|
+
index = sqlite3_bind_parameter_index(@handle.value, ":#{name}")
|
|
71
|
+
raise SQLite3Error, "couldn't find index for #{name}!" if index == 0
|
|
72
|
+
|
|
73
|
+
when Integer
|
|
74
|
+
index = name
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
index
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def prepare(sql)
|
|
81
|
+
remainder = Pointer.new(:string)
|
|
82
|
+
result = sqlite3_prepare_v2(@db.value, sql, -1, @handle, remainder)
|
|
83
|
+
raise SQLite3Error, sqlite3_errmsg(@db.value) if result != SQLITE_OK
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|