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 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
@@ -0,0 +1,17 @@
1
+ .repl_history
2
+ build
3
+ tags
4
+ app/pixate_code.rb
5
+ resources/*.nib
6
+ resources/*.momd
7
+ resources/*.storyboardc
8
+ vendor/sqlite3/build-*
9
+ .DS_Store
10
+ nbproject
11
+ .redcar
12
+ #*#
13
+ *~
14
+ *.sw[po]
15
+ .eprj
16
+ .sass-cache
17
+ .idea
data/.rvmrc ADDED
@@ -0,0 +1,4 @@
1
+ rvm ruby-1.9.3-p194@rubymotion --create
2
+
3
+ [[ -s "config/rvm_env.sh" ]] && . "config/rvm_env.sh"
4
+ [[ -s ".rvm_env" ]] && . ".rvm_env"
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gem 'rake', '~> 0.9.0'
5
+
6
+ gem 'cocoapods'#, '~> 0.16.0'
7
+ gem 'motion-cocoapods'#, '~> 1.2.1'
8
+
9
+ gem 'guard-motion'
10
+ gem 'rb-fsevent', '~> 0.9.1'
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
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'motion', :all_after_pass => false do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+
7
+ # RubyMotion gem example
8
+ watch(%r{^lib/[^/]+/(.+)\.rb$}) { |m| "./spec/#{m[1]}_spec.rb" }
9
+ end
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
@@ -0,0 +1,5 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ true
4
+ end
5
+ end
@@ -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,4 @@
1
+ module SQLite3
2
+ class SQLite3Error < RuntimeError
3
+ end
4
+ 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