micro_sql 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler"
12
+ gem "jeweler"
13
+ # gem "rcov", ">= 0"
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.8.3)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rdoc
10
+ json (1.6.6)
11
+ rake (0.9.2.2)
12
+ rdoc (3.12)
13
+ json (~> 1.4)
14
+ shoulda (3.0.1)
15
+ shoulda-context (~> 1.0.0)
16
+ shoulda-matchers (~> 1.0.0)
17
+ shoulda-context (1.0.0)
18
+ shoulda-matchers (1.0.0)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ bundler
25
+ jeweler
26
+ rdoc (~> 3.12)
27
+ shoulda
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 radiospiel
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.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = micro_sql
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to micro_sql
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
+ * Fork the project.
10
+ * Start a feature/bugfix branch.
11
+ * Commit and push until you are happy with your contribution.
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2012 radiospiel. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "micro_sql"
18
+ gem.homepage = "http://github.com/radiospiel/micro_sql"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A minimal ruby interface for SQL databases}
21
+ gem.description = %Q{You only need a single method to talk to your database...}
22
+ gem.email = "eno@open-lab.org"
23
+ gem.authors = ["radiospiel"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ # require 'rcov/rcovtask'
36
+ # Rcov::RcovTask.new do |test|
37
+ # test.libs << 'test'
38
+ # test.pattern = 'test/**/test_*.rb'
39
+ # test.verbose = true
40
+ # test.rcov_opts << '--exclude "gems/*"'
41
+ # end
42
+
43
+ task :default => :test
44
+
45
+ require 'rdoc/task'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "micro_sql #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,65 @@
1
+ require 'pg'
2
+
3
+ class MicroSql::PgAdapter < MicroSql
4
+ def initialize(url)
5
+ uri = URI.parse(url)
6
+
7
+ @impl = PG.connect :host => uri.host,
8
+ :port => uri.port || 5433,
9
+ :user => uri.user,
10
+ :password => uri.password,
11
+ :dbname => uri.path[1..-1]
12
+ end
13
+
14
+ def tables
15
+ exec("SELECT tablename FROM pg_tables WHERE tablename NOT LIKE 'pg_%' AND tablename NOT LIKE 'sql_%'").map(&:first)
16
+ end
17
+
18
+ def execute_batch(sql)
19
+ sql.split(";").each { exec(part) }
20
+ end
21
+
22
+ private
23
+
24
+ def execute(flag, sql, *args)
25
+ statement = prepare(sql) if flag == :prepare
26
+ result = if statement
27
+ @impl.exec_prepared(statement, args)
28
+ else
29
+ @impl.exec(sql, args)
30
+ end
31
+
32
+ if sql =~ /^\s*(UPDATE|DELETE|INSERT)/i
33
+ return result.cmd_tuples
34
+ end
35
+
36
+ types = (0 ... result.num_fields).map do |i|
37
+ # Get the type of the second column of the result 'res'
38
+ @impl.exec( "SELECT format_type($1,$2)", [result.ftype(i), result.fmod(i)] ).getvalue( 0, 0 )
39
+ end
40
+
41
+ result.values.map do |rec|
42
+ types.each_with_index do |type, idx|
43
+ case type
44
+ when "text", "name" then :nop
45
+ when "integer", "bigint" then rec[idx] = rec[idx] && rec[idx].to_i
46
+ else raise "Unknown column type #{type}: #{rec[idx].inspect}"
47
+ end
48
+ end
49
+
50
+ rec
51
+ end
52
+ end
53
+
54
+ def prepare_query(key, sql)
55
+ idx = 0
56
+ query = sql.gsub("?") do
57
+ idx += 1
58
+ "$#{idx}"
59
+ end
60
+
61
+ return nil if idx == 0
62
+ @impl.prepare(key, query)
63
+ key
64
+ end
65
+ end
@@ -0,0 +1,56 @@
1
+ # -- The settings table; has support for ttl etc. ---------------------------
2
+
3
+ require "json"
4
+
5
+ class MicroSql::SettingsTable < MicroSql::Table
6
+ def initialize(db)
7
+ super db, "CREATE TABLE settings(uid TEXT PRIMARY KEY, value TEXT, ttl BIGINT)"
8
+ end
9
+
10
+ def [](key)
11
+ value, ttl = db.ask("SELECT value, ttl FROM settings WHERE uid=?", key)
12
+ decode(value) if !ttl || ttl < Time.now.to_i
13
+ end
14
+
15
+ def cached(key, ttl, &block)
16
+ update(key, yield, ttl)
17
+ end
18
+
19
+ def update(key, value, ttl = nil)
20
+ if value
21
+ encoded = encode(value)
22
+ ttl += Time.now.to_i if ttl
23
+ affected = @db.ask("UPDATE settings SET value=?, ttl=? WHERE uid=?", encoded, ttl, key)
24
+ if affected == 0
25
+ @db.ask("INSERT INTO settings(value, ttl, uid) VALUES(?,?,?)", encoded, ttl, key)
26
+ end
27
+ else
28
+ @db.ask("DELETE FROM settings WHERE uid=?", key)
29
+ end
30
+
31
+ value
32
+ end
33
+
34
+ alias :[]= :update
35
+
36
+ def decode(io)
37
+ return unless io
38
+ JSON.parse("[#{io}]").first
39
+ end
40
+
41
+ def encode(value)
42
+ value && value.to_json
43
+ end
44
+ end
45
+
46
+ require "forwardable"
47
+
48
+ class MicroSql::Settings
49
+ def initialize(url)
50
+ @db = MicroSql.create(url)
51
+ @settings_table = MicroSql::SettingsTable.new @db
52
+ end
53
+
54
+ extend Forwardable
55
+ delegate [ :[], :cached, :update, :[]= ] => :@settings_table
56
+ end
@@ -0,0 +1,32 @@
1
+ require "sqlite3"
2
+
3
+ class MicroSql::SqliteAdapter < MicroSql
4
+ def initialize(url)
5
+ uri = URI.parse(url)
6
+ @impl = SQLite3::Database.new(uri.path)
7
+ exec "PRAGMA synchronous = OFF"
8
+ end
9
+
10
+ def execute_batch(sql)
11
+ @impl.execute_batch(sql)
12
+ end
13
+
14
+ def tables
15
+ exec("SELECT name FROM sqlite_master WHERE type=?", "table").map(&:first)
16
+ end
17
+
18
+ def exec(sql, *args)
19
+ results = prepare(sql).execute!(*args)
20
+ case sql
21
+ when /^\s*INSERT/i then @impl.last_insert_row_id
22
+ when /^\s*(UPDATE|DELETE)/i then @impl.changes
23
+ else results
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def prepare_query(key, sql)
30
+ @impl.prepare(sql)
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ # -- A basic table ----------------------------------------------------------
2
+
3
+ class MicroSql::Table
4
+ attr :db, :table_name
5
+
6
+ def initialize(db, sql)
7
+ @db = db
8
+ if sql =~ /\s*CREATE TABLE\s+([^\( ]+)/
9
+ @table_name = $1
10
+ end
11
+
12
+ raise ArgumentError, "Cannot determine table_name from SQL: #{sql}" unless table_name
13
+
14
+ build(sql) unless exists?
15
+ end
16
+
17
+ private
18
+
19
+ def exists?
20
+ db.tables.include?(table_name.to_s)
21
+ end
22
+
23
+ def build(sql)
24
+ LOGGER.warn "Create table #{table_name}"
25
+ db.execute_batch(sql)
26
+ end
27
+ end
data/lib/micro_sql.rb ADDED
@@ -0,0 +1,69 @@
1
+ require "uri"
2
+
3
+ class MicroSql
4
+ end
5
+
6
+ require_relative "micro_sql/pg_adapter"
7
+ require_relative "micro_sql/sqlite_adapter"
8
+ require_relative "micro_sql/table"
9
+ require_relative "micro_sql/settings"
10
+
11
+ class MicroSql
12
+
13
+ (class << self; self; end).class_eval do
14
+ private :new
15
+
16
+ def create(url)
17
+ scheme = URI.parse(url).scheme
18
+ klass_name = if !scheme
19
+ "MicroSql::SqliteAdapter"
20
+ else
21
+ "MicroSql::#{scheme[0...1].upcase}#{scheme[1..-1]}Adapter"
22
+ end
23
+
24
+ eval(klass_name).send(:new, url)
25
+ end
26
+ end
27
+
28
+ def ask(sql, *args)
29
+ r = exec(sql, *args)
30
+ return r unless r.is_a?(Array)
31
+
32
+ r = r.first
33
+ return r unless r.is_a?(Array)
34
+ return r unless r.length == 1
35
+ r.first
36
+ end
37
+
38
+ def exec!(sql, *args)
39
+ execute :no_prepare, sql, *args
40
+ end
41
+
42
+ def exec(sql, *args)
43
+ execute :prepare, sql, *args
44
+ end
45
+
46
+ def transaction(&block)
47
+ @impl.transaction(&block)
48
+ end
49
+
50
+ def settings
51
+ @settings ||= MicroSql::SettingsTable.new(self)
52
+ end
53
+
54
+ private
55
+
56
+ def prepared_queries
57
+ @prepared_queries ||= {}
58
+ end
59
+
60
+ def prepare(sql)
61
+ key = sql.to_uid.to_s
62
+ prepared_queries.fetch(key)
63
+ rescue KeyError
64
+ prepared_queries[key] = prepare_query(key, sql)
65
+ end
66
+ end
67
+
68
+ # url = "pg://sqdb:sqdb@localhost:5433/sqdb"
69
+ # MicroSql.create(url)
data/micro_sql.gemspec ADDED
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "micro_sql"
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["radiospiel"]
12
+ s.date = "2012-03-30"
13
+ s.description = "You only need a single method to talk to your database..."
14
+ s.email = "eno@open-lab.org"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/micro_sql.rb",
28
+ "lib/micro_sql/pg_adapter.rb",
29
+ "lib/micro_sql/settings.rb",
30
+ "lib/micro_sql/sqlite_adapter.rb",
31
+ "lib/micro_sql/table.rb",
32
+ "micro_sql.gemspec",
33
+ "test/helper.rb",
34
+ "test/test_micro_sql.rb"
35
+ ]
36
+ s.homepage = "http://github.com/radiospiel/micro_sql"
37
+ s.licenses = ["MIT"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = "1.8.17"
40
+ s.summary = "A minimal ruby interface for SQL databases"
41
+
42
+ if s.respond_to? :specification_version then
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
47
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
48
+ s.add_development_dependency(%q<bundler>, [">= 0"])
49
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<shoulda>, [">= 0"])
52
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
53
+ s.add_dependency(%q<bundler>, [">= 0"])
54
+ s.add_dependency(%q<jeweler>, [">= 0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<shoulda>, [">= 0"])
58
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
59
+ s.add_dependency(%q<bundler>, [">= 0"])
60
+ s.add_dependency(%q<jeweler>, [">= 0"])
61
+ end
62
+ end
63
+
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'micro_sql'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestMicroSql < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: micro_sql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - radiospiel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-30 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: shoulda
16
+ requirement: &70301986679420 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70301986679420
25
+ - !ruby/object:Gem::Dependency
26
+ name: rdoc
27
+ requirement: &70301986678800 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '3.12'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70301986678800
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ requirement: &70301986678140 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70301986678140
47
+ - !ruby/object:Gem::Dependency
48
+ name: jeweler
49
+ requirement: &70301986677540 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70301986677540
58
+ description: You only need a single method to talk to your database...
59
+ email: eno@open-lab.org
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files:
63
+ - LICENSE.txt
64
+ - README.rdoc
65
+ files:
66
+ - .document
67
+ - Gemfile
68
+ - Gemfile.lock
69
+ - LICENSE.txt
70
+ - README.rdoc
71
+ - Rakefile
72
+ - VERSION
73
+ - lib/micro_sql.rb
74
+ - lib/micro_sql/pg_adapter.rb
75
+ - lib/micro_sql/settings.rb
76
+ - lib/micro_sql/sqlite_adapter.rb
77
+ - lib/micro_sql/table.rb
78
+ - micro_sql.gemspec
79
+ - test/helper.rb
80
+ - test/test_micro_sql.rb
81
+ homepage: http://github.com/radiospiel/micro_sql
82
+ licenses:
83
+ - MIT
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ segments:
95
+ - 0
96
+ hash: 4072090889044300624
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.17
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: A minimal ruby interface for SQL databases
109
+ test_files: []