simple-sql 0.1.3

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: a64abeaf64f527b10d9381fcecbb47c712184ebc
4
+ data.tar.gz: 8ad02d882e3f281c4880af342c7b15c33e189e4f
5
+ SHA512:
6
+ metadata.gz: fec8ff260a4551b5f5e73640291cf1db92792aa108fc73e218825e27890e8275028617c52fa9acde204373155bebad645d894515461bf09dc1f2ccac592efa7e
7
+ data.tar.gz: fb5648febf02c8a08e25c31e30602838bdaff934ddd1097d597291b3b09a03d8451218f1a9594a77ac0fe66be9b2366ce1406477e2b1fa3dc72e7ff30f5cf97e
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ coverage
2
+ rdoc
3
+ pkg
4
+ log/test.log
data/.rubocop.yml ADDED
@@ -0,0 +1,35 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Exclude:
4
+ - 'spec/**/*'
5
+ - 'test/**/*'
6
+ - 'bin/**/*'
7
+ - 'tasks/release.rake'
8
+ - '*.gemspec'
9
+ - 'Gemfile'
10
+ - 'Rakefile'
11
+
12
+ Metrics/LineLength:
13
+ Max: 115
14
+
15
+ Style/SpecialGlobalVars:
16
+ Enabled: false
17
+
18
+ Style/StringLiterals:
19
+ EnforcedStyle: double_quotes
20
+ ConsistentQuotesInMultiline: false
21
+
22
+ Style/ClassAndModuleChildren:
23
+ Enabled: false
24
+
25
+ Style/ModuleFunction:
26
+ Enabled: false
27
+
28
+ Style/FrozenStringLiteralComment:
29
+ Enabled: false
30
+
31
+ Style/Documentation:
32
+ Enabled: false
33
+
34
+ Style/MutableConstant:
35
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in {gemname}.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ simple-sql (0.1.3)
5
+ activerecord (~> 4)
6
+ pg_array_parser (~> 0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (4.2.10)
12
+ activesupport (= 4.2.10)
13
+ builder (~> 3.1)
14
+ activerecord (4.2.10)
15
+ activemodel (= 4.2.10)
16
+ activesupport (= 4.2.10)
17
+ arel (~> 6.0)
18
+ activesupport (4.2.10)
19
+ i18n (~> 0.7)
20
+ minitest (~> 5.1)
21
+ thread_safe (~> 0.3, >= 0.3.4)
22
+ tzinfo (~> 1.1)
23
+ arel (6.0.4)
24
+ ast (2.3.0)
25
+ builder (3.2.3)
26
+ concurrent-ruby (1.0.5)
27
+ database_cleaner (1.6.2)
28
+ diff-lcs (1.3)
29
+ docile (1.1.5)
30
+ factory_girl (4.8.0)
31
+ activesupport (>= 3.0.0)
32
+ i18n (0.9.1)
33
+ concurrent-ruby (~> 1.0)
34
+ json (2.1.0)
35
+ minitest (5.10.3)
36
+ parallel (1.10.0)
37
+ parser (2.4.0.2)
38
+ ast (~> 2.3)
39
+ pg (0.20.0)
40
+ pg_array_parser (0.0.9)
41
+ powerpack (0.1.1)
42
+ rainbow (2.2.2)
43
+ rake
44
+ rake (11.3.0)
45
+ rspec (3.7.0)
46
+ rspec-core (~> 3.7.0)
47
+ rspec-expectations (~> 3.7.0)
48
+ rspec-mocks (~> 3.7.0)
49
+ rspec-core (3.7.0)
50
+ rspec-support (~> 3.7.0)
51
+ rspec-expectations (3.7.0)
52
+ diff-lcs (>= 1.2.0, < 2.0)
53
+ rspec-support (~> 3.7.0)
54
+ rspec-mocks (3.7.0)
55
+ diff-lcs (>= 1.2.0, < 2.0)
56
+ rspec-support (~> 3.7.0)
57
+ rspec-support (3.7.0)
58
+ rubocop (0.52.1)
59
+ parallel (~> 1.10)
60
+ parser (>= 2.4.0.2, < 3.0)
61
+ powerpack (~> 0.1)
62
+ rainbow (>= 2.2.2, < 4.0)
63
+ ruby-progressbar (~> 1.7)
64
+ unicode-display_width (~> 1.0, >= 1.0.1)
65
+ ruby-progressbar (1.9.0)
66
+ simplecov (0.15.1)
67
+ docile (~> 1.1.0)
68
+ json (>= 1.8, < 3)
69
+ simplecov-html (~> 0.10.0)
70
+ simplecov-html (0.10.2)
71
+ thread_safe (0.3.6)
72
+ tzinfo (1.2.4)
73
+ thread_safe (~> 0.1)
74
+ unicode-display_width (1.1.3)
75
+
76
+ PLATFORMS
77
+ ruby
78
+
79
+ DEPENDENCIES
80
+ database_cleaner (~> 1)
81
+ factory_girl (= 4.8.0)
82
+ pg (= 0.20)
83
+ rake (~> 11)
84
+ rspec (~> 3.7)
85
+ rubocop (~> 0.52.1)
86
+ simple-sql!
87
+ simplecov (~> 0)
88
+
89
+ BUNDLED WITH
90
+ 1.13.6
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # simple-sql
2
+
3
+ ## Installation
4
+
5
+ The gem is available through our private gem host:
6
+
7
+ ```ruby
8
+ gem 'simple-sql' # + version
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ This gem defines a module `Simple::SQL`, which you can use to execute SQL statements
14
+ on the current ActiveRecord connection.
15
+
16
+ Simple::SQL takes care of converting arguments back and forth.
17
+
18
+ ### Running a query
19
+
20
+ `Simple::SQL.all` runs a query, with optional arguments, and returns the result. Usage example:
21
+
22
+ Simple::SQL.all "SELECT id, email FROM users WHERE id = ANY($1)", [1,2,3]
23
+
24
+ If the SQL query returns rows with one column, this method returns an array of these values.
25
+ Otherwise it returns an array of arrays.
26
+
27
+ Examples:
28
+
29
+ ```ruby
30
+ Simple::SQL.all("SELECT id FROM users") # returns an array of id values, but
31
+ Simple::SQL.all("SELECT id, email FROM users") # returns an array of arrays `[ <id>, <email> ]`.
32
+ ```
33
+
34
+ If a block is passed to SQL.all, each row is yielded into the block:
35
+
36
+ ```ruby
37
+ Simple::SQL.all "SELECT id, email FROM users" do |id, email|
38
+ # do something
39
+ end
40
+ ```
41
+
42
+ In this case SQL.all returns `self`, which lets you chain function calls.
43
+
44
+ ### Getting the first result
45
+
46
+ `Simple::SQL.ask` returns runs a query, with optional arguments, and returns the first result row.
47
+
48
+
49
+ Simple::SQL.ask "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1", [1,2,3]
50
+
51
+ If the SQL query returns rows with one column, this method returns the column value of the first row; otherwise it returns an array (or `nil` if there was no result).
52
+
53
+ Examples:
54
+
55
+ ```ruby
56
+ Simple::SQL.ask "SELECT id FROM users WHERE email=$1", "foo@local" # returns a number (or `nil`) and
57
+ Simple::SQL.ask "SELECT id, email FROM users WHERE email=$?", "foo@local" # returns an array `[ <id>, <email> ]` (or `nil`)
58
+ ```
59
+
60
+ ## Notes
61
+
62
+ Remember that Postgresql uses $1, $2, etc. as placeholders; the following is correct:
63
+
64
+ ```ruby
65
+ Simple::SQL.all "SELECT * FROM users WHERE email=$1", "foo@bar.local"
66
+ ```
67
+
68
+ Also note that `IN(?)` is not supported by the Postgresql client library; instead you
69
+ must use `= ANY`, for example:
70
+
71
+ ```ruby
72
+ Simple::SQL.all "SELECT * FROM users WHERE id = ANY($1)", [1,2,3]
73
+ ```
74
+
75
+
76
+ ## Bugs and Limitations
77
+
78
+ ### 1. Multiple connections
79
+
80
+ It is currently not possible to run SQL queries against a database which is not
81
+ connected via ActiveRecord::Base.connection.
82
+
83
+ ### 2. Postgresql only
84
+
85
+ Only Postgresql is supported.
86
+
87
+ ### 3. Limited support for types
88
+
89
+ This gem does not use `pg`'s support for encoding and decoding types, since
90
+ that might probably interfere with how ActiveRecord is setting up the `pg`
91
+ gem.
92
+
93
+ It therefore assumes ActiveRecord is used in the same project, which sets up
94
+ pg to not decode data in any meaningful way, and provides some code to decode
95
+ the data returned from the database. Only a handful of types is currently
96
+ supported by the Decoder - it is fairly easy to add new types, though.
97
+
98
+ ### 4. text arrays
99
+
100
+ The library used to parse array results seems to be buggy if the array contains
101
+ strings containing the "`" character.
102
+
103
+ ## Test
104
+
105
+ 1. `createdb simple-sql-test`
106
+ 2. `bundle install`
107
+ 3. `bin/rspec`
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "rubocop/rake_task"
2
+ require "rspec/core/rake_task"
3
+
4
+ Dir.glob("tasks/*.rake").each { |r| import r }
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ RuboCop::RakeTask.new(:rubocop)
8
+
9
+ task default: [:spec, :rubocop]
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require "pathname"
10
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require "rubygems"
14
+ require "bundler/setup"
15
+
16
+ load Gem.bin_path("rake", "rake")
@@ -0,0 +1,7 @@
1
+ adapter: postgresql
2
+ encoding: utf8
3
+ host: '127.0.0.1'
4
+ username: postgres
5
+ pool: 5
6
+ timeout: 5000
7
+ database: simple-sql-test
data/lib/simple/sql.rb ADDED
@@ -0,0 +1,103 @@
1
+ require_relative "sql/version.rb"
2
+ require_relative "sql/decoder.rb"
3
+ require_relative "sql/encoder.rb"
4
+
5
+ module Simple
6
+ # The Simple::SQL module
7
+ module SQL
8
+ extend self
9
+
10
+ # execute one or more sql statements. This method does not allow to pass in
11
+ # arguments - since the pg client does not support this - but it allows to
12
+ # run multiple sql statements separated by ";"
13
+ extend Forwardable
14
+ delegate exec: :pg
15
+
16
+ # Runs a query, with optional arguments, and returns the result. If the SQL
17
+ # query returns rows with one column, this method returns an array of these
18
+ # values. Otherwise it returns an array of arrays.
19
+ #
20
+ # Example:
21
+ #
22
+ # - <tt>Simple::SQL.all("SELECT id FROM users")</tt> returns an array of id values
23
+ # - <tt>Simple::SQL.all("SELECT id, email FROM users")</tt> returns an array of
24
+ # arrays `[ <id>, <email> ]`.
25
+ #
26
+ # Simple::SQL.all "SELECT id, email FROM users" do |id, email|
27
+ # # do something
28
+ # end
29
+
30
+ def all(sql, *args, &block)
31
+ result = connection.exec_params(sql, Encoder.encode_args(args))
32
+ decoder = Decoder.new(result)
33
+
34
+ enumerate(result, decoder, block)
35
+ end
36
+
37
+ # Runs a query and returns the first result row of a query.
38
+ #
39
+ # Examples:
40
+ #
41
+ # - <tt>Simple::SQL.ask "SELECT id FROM users WHERE email=$?", "foo@local"</tt>
42
+ # returns a number (or +nil+)
43
+ # - <tt>Simple::SQL.ask "SELECT id, email FROM users WHERE email=$?", "foo@local"</tt>
44
+ # returns an array <tt>[ <id>, <email> ]</tt> (or +nil+)
45
+ def ask(sql, *args)
46
+ catch(:ok) do
47
+ all(sql, *args) { |row| throw :ok, row }
48
+ nil
49
+ end
50
+ end
51
+
52
+ # Runs a query, with optional arguments, and returns the result as an
53
+ # array of Hashes.
54
+ #
55
+ # Example:
56
+ #
57
+ # - <tt>Simple::SQL.records("SELECT id, email FROM users")</tt> returns an array of
58
+ # hashes { id: id, email: email }
59
+ #
60
+ # Simple::SQL.records "SELECT id, email FROM users" do |record|
61
+ # # do something
62
+ # end
63
+
64
+ def records(sql, *args, into: Hash, &block)
65
+ result = connection.exec_params(sql, Encoder.encode_args(args))
66
+ decoder = Decoder.new(result, :record, into: into)
67
+
68
+ enumerate(result, decoder, block)
69
+ end
70
+
71
+ # Runs a query and returns the first result row of a query as a Hash.
72
+ def record(sql, *args, into: Hash)
73
+ catch(:ok) do
74
+ records(sql, *args, into: into) { |row| throw :ok, row }
75
+ nil
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def enumerate(result, decoder, block)
82
+ if block
83
+ result.each_row do |row|
84
+ block.call decoder.decode(row)
85
+ end
86
+ self
87
+ else
88
+ ary = []
89
+ result.each_row { |row| ary << decoder.decode(row) }
90
+ ary
91
+ end
92
+ end
93
+
94
+ def resolve_type(ftype, fmod)
95
+ @resolved_types ||= {}
96
+ @resolved_types[[ftype, fmod]] ||= connection.exec("SELECT format_type($1,$2)", [ftype, fmod]).getvalue(0, 0)
97
+ end
98
+
99
+ def connection
100
+ ActiveRecord::Base.connection.raw_connection
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,114 @@
1
+ # private
2
+ module Simple::SQL::Decoder
3
+ extend self
4
+
5
+ def new(result, mode = nil, into: nil)
6
+ if mode == :record then Record.new(result, into: into)
7
+ elsif result.nfields == 1 then SingleColumn.new(result)
8
+ else MultiColumns.new(result)
9
+ end
10
+ end
11
+
12
+ # rubocop:disable Metrics/AbcSize
13
+ # rubocop:disable Metrics/CyclomaticComplexity
14
+ # rubocop:disable Metrics/MethodLength
15
+ def decode_value(type, s)
16
+ case type
17
+ when :unknown then s
18
+ when :"character varying" then s
19
+ when :integer then Integer(s)
20
+ when :bigint then Integer(s)
21
+ when :numeric then Float(s)
22
+ when :'integer[]' then s.scan(/-?\d+/).map { |part| Integer(part) }
23
+ when :"character varying[]" then parse_pg_array(s)
24
+ when :"text[]" then parse_pg_array(s)
25
+ when :"timestamp without time zone" then ::Time.parse(s)
26
+ when :hstore then HStore.parse(s)
27
+ when :json then ::JSON.parse(s)
28
+ when :jsonb then ::JSON.parse(s)
29
+ else s # unknown value, we just return the string here.
30
+ end
31
+ end
32
+ # rubocop:enable Metrics/AbcSize
33
+ # rubocop:enable Metrics/CyclomaticComplexity
34
+ # rubocop:enable Metrics/MethodLength
35
+
36
+ require "pg_array_parser"
37
+ extend PgArrayParser
38
+
39
+ # HStore parsing
40
+ module HStore
41
+ module_function
42
+
43
+ # thanks to https://github.com/engageis/activerecord-postgres-hstore for regexps!
44
+
45
+ QUOTED_LITERAL = /"[^"\\]*(?:\\.[^"\\]*)*"/
46
+ UNQUOTED_LITERAL = /[^\s=,][^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
47
+ LITERAL = /(#{QUOTED_LITERAL}|#{UNQUOTED_LITERAL})/
48
+ PAIR = /#{LITERAL}\s*=>\s*#{LITERAL}/
49
+ NULL = /\ANULL\z/i
50
+ DOUBLE_QUOTE = '"'.freeze
51
+ ESCAPED_CHAR = /\\(.)/
52
+
53
+ def parse(hstore)
54
+ hstore.scan(PAIR).each_with_object({}) do |(k, v), memo|
55
+ k = unpack(k)
56
+ k = k.to_sym
57
+ v = v =~ NULL ? nil : unpack(v)
58
+ memo[k] = v
59
+ end
60
+ end
61
+
62
+ def unpack(string)
63
+ string = string[1..-2] if string[0] == DOUBLE_QUOTE # remove quotes, if any
64
+ string.gsub ESCAPED_CHAR, '\1' # unescape, if necessary
65
+ end
66
+ end
67
+ end
68
+
69
+ class Simple::SQL::Decoder::MultiColumns
70
+ def initialize(result)
71
+ @field_types = 0.upto(result.fields.length - 1).map do |idx|
72
+ typename = ::Simple::SQL.send(:resolve_type, result.ftype(idx), result.fmod(idx))
73
+ typename.to_sym
74
+ end
75
+ end
76
+
77
+ def decode(row)
78
+ @field_types.zip(row).map do |field_type, value|
79
+ value && Simple::SQL::Decoder.decode_value(field_type, value)
80
+ end
81
+ end
82
+ end
83
+
84
+ class Simple::SQL::Decoder::SingleColumn < Simple::SQL::Decoder::MultiColumns
85
+ def initialize(result)
86
+ super
87
+ @field_type = @field_types.first
88
+ end
89
+
90
+ def decode(row)
91
+ value = row.first
92
+ value && Simple::SQL::Decoder.decode_value(@field_type, value)
93
+ end
94
+ end
95
+
96
+ class Simple::SQL::Decoder::Record < Simple::SQL::Decoder::MultiColumns
97
+ def initialize(result, into:)
98
+ super(result)
99
+
100
+ @into = into
101
+ @result = result
102
+ @field_names = @result.fields.map(&:to_sym)
103
+ end
104
+
105
+ def decode(row)
106
+ decoded_row = super(row)
107
+ hsh = Hash[@field_names.zip(decoded_row)]
108
+ if @into && @into != Hash
109
+ @into.new(hsh)
110
+ else
111
+ hsh
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,21 @@
1
+ # private
2
+ module Simple::SQL::Encoder
3
+ extend self
4
+ extend Forwardable
5
+
6
+ delegate connection: ::Simple::SQL
7
+
8
+ def encode_args(args)
9
+ args.map { |arg| encode_arg(arg) }
10
+ end
11
+
12
+ def encode_arg(arg)
13
+ return arg unless arg.is_a?(Array)
14
+
15
+ if arg.first.is_a?(String)
16
+ "{#{arg.map { |a| "\"#{connection.escape(a)}\"" }.join(',')}}"
17
+ else
18
+ "{#{arg.join(',')}}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ module Simple
2
+ module SQL
3
+ VERSION = "0.1.3"
4
+ end
5
+ end
data/log/.gitkeep ADDED
File without changes
@@ -0,0 +1,43 @@
1
+ # This file is part of the sinatra-sse ruby gem.
2
+ #
3
+ # Copyright (c) 2016, 2017 @radiospiel, mediapeers Gem
4
+ # Distributed under the terms of the modified BSD license, see LICENSE.BSD
5
+
6
+ lib = File.expand_path('../lib', __FILE__)
7
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
8
+ require 'simple/sql/version'
9
+
10
+ Gem::Specification.new do |gem|
11
+ gem.name = "simple-sql"
12
+ gem.version = Simple::SQL::VERSION
13
+
14
+ gem.authors = [ "radiospiel", "mediapeers GmbH" ]
15
+ gem.email = "eno@radiospiel.org"
16
+ gem.homepage = "http://github.com/radiospiel/simple-sql"
17
+ gem.summary = "SQL with a simple interface"
18
+
19
+ gem.description = "SQL with a simple interface. Postgres only."
20
+
21
+ gem.files = `git ls-files`.split($/)
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = %w(lib)
24
+
25
+ # executables are used for development purposes only
26
+ gem.executables = []
27
+
28
+ gem.required_ruby_version = '~> 2.3'
29
+
30
+ gem.add_dependency 'activerecord', '~> 4'
31
+ gem.add_dependency 'pg_array_parser', '~> 0'
32
+
33
+ # optional gems (required by some of the parts)
34
+
35
+ # development gems
36
+ gem.add_development_dependency 'pg', '0.20'
37
+ gem.add_development_dependency 'rake', '~> 11'
38
+ gem.add_development_dependency 'rspec', '~> 3.7'
39
+ gem.add_development_dependency 'rubocop', '~> 0.52.1'
40
+ gem.add_development_dependency 'factory_girl', '4.8.0'
41
+ gem.add_development_dependency 'database_cleaner', '~> 1'
42
+ gem.add_development_dependency 'simplecov', '~> 0'
43
+ end
@@ -0,0 +1,10 @@
1
+ require "spec_helper"
2
+
3
+ describe Simple::SQL do
4
+ describe "VERSION" do
5
+ it "defines a version string" do
6
+ # Note: this allows for 0.12.34beta
7
+ expect(Simple::SQL::VERSION).to match(/^\d+\.\d+\.\d+/)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ describe "Simple::SQL.ask" do
4
+ USER_COUNT = 2
5
+
6
+ def expects(expected_result, sql, *args)
7
+ expect(Simple::SQL.ask(sql, *args)).to eq(expected_result)
8
+ end
9
+
10
+ let!(:users) { 1.upto(USER_COUNT).map { create(:user) } }
11
+
12
+ it "calls the database" do
13
+ expect(User.count).to eq(2)
14
+
15
+ expects 2, "SELECT COUNT(*) FROM users"
16
+ expects 1, "SELECT COUNT(*) FROM users WHERE id=$1", users.first.id
17
+ expects 0, "SELECT COUNT(*) FROM users WHERE id=$1", -1
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+
3
+ describe "Simple::SQL conversions" do
4
+ def expects(expected_result, sql, *args)
5
+ expect(Simple::SQL.ask(sql, *args)).to eq(expected_result)
6
+ end
7
+
8
+ describe "data conversions" do
9
+ it "returns arrays when asking for multiple columns" do
10
+ expects ["one", "two", 3.5], "SELECT 'one', 'two', 3.5"
11
+ end
12
+
13
+ it "converts text arrays" do
14
+ expects ["foo,bar"], "SELECT ARRAY[$1::varchar]", "foo,bar"
15
+ expects %w(foo bar), "SELECT $1::varchar[]", %w(foo bar)
16
+ end
17
+
18
+ it "converts integer arrays" do
19
+ expects [1, 2, 3], "SELECT ARRAY[1,2,3]"
20
+ end
21
+
22
+ it "converts weird strings properly" do
23
+ expects "foo,\"bar}", "SELECT $1::varchar", "foo,\"bar}"
24
+ expects ["one", "two", "3.5", "foo,\"bar}"], "SELECT ARRAY['one', 'two', '3.5', 'foo,\"bar}']"
25
+ expects ["foo", "foo,bar}"], "SELECT $1::varchar[]", ["foo", "foo,bar}"]
26
+ end
27
+
28
+ it "parses JSON as expected" do
29
+ expects({"a"=>1, "b"=>2}, 'SELECT \'{"a":1,"b":2}\'::json')
30
+ expects({"a"=>1, "b"=>2}, 'SELECT \'{"a":1,"b":2}\'::jsonb')
31
+ end
32
+
33
+ xit "fails sometimes w/ malformed array literal, pt. 1" do
34
+ expects 0, 'SELECT $1::varchar[]', [ "foo", 'foo,"bar}' ]
35
+ end
36
+
37
+ xit "fails sometimes w/ malformed array literal, pt. 2" do
38
+ expects 0, 'SELECT $1::varchar[]', [ "foo", 'foo,"bar}' ]
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+
3
+ describe "Simple::SQL.record" do
4
+ USER_COUNT = 2
5
+
6
+ def expects(expected_result, sql, *args)
7
+ expect(Simple::SQL.record(sql, *args)).to eq(expected_result)
8
+ end
9
+
10
+ let!(:users) { 1.upto(USER_COUNT).map { create(:user) } }
11
+
12
+ before do
13
+ expect(User.count).to eq(2)
14
+ end
15
+
16
+ it "calls the database" do
17
+ r = Simple::SQL.record("SELECT COUNT(*) AS count FROM users")
18
+ expect(r).to eq({count: 2})
19
+ end
20
+
21
+ it "supports the into: option" do
22
+ r = Simple::SQL.record("SELECT COUNT(*) AS count FROM users", into: OpenStruct)
23
+ expect(r).to be_a(OpenStruct)
24
+ expect(r).to eq(OpenStruct.new(count: 2))
25
+ end
26
+
27
+ it "supports the into: option even with parameters" do
28
+ r = Simple::SQL.record("SELECT $1::integer AS count FROM users", 2, into: OpenStruct)
29
+ expect(r).to be_a(OpenStruct)
30
+ expect(r).to eq(OpenStruct.new(count: 2))
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ %w(auth authentication authorization).each do |library_name|
2
+ path = File.expand_path("../../#{library_name}/lib", __FILE__)
3
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
4
+ end
5
+
6
+ ENV["RACK_ENV"] = "test"
7
+ ENV["RAILS_ENV"] = "test"
8
+
9
+ require "rspec"
10
+
11
+ Dir.glob("./spec/support/**/*.rb").sort.each { |path| load path }
12
+
13
+ require "simple/sql"
14
+
15
+ RSpec.configure do |config|
16
+ config.run_all_when_everything_filtered = true
17
+ config.filter_run focus: (ENV["CI"] != "true")
18
+ config.expect_with(:rspec) { |c| c.syntax = :expect }
19
+ config.include FactoryGirl::Syntax::Methods
20
+ config.order = "random"
21
+ end
@@ -0,0 +1,35 @@
1
+ # connect to the database and setup the schema
2
+ require "active_record"
3
+
4
+ ActiveRecord::Base.establish_connection(YAML.load_file("config/database.yml"))
5
+
6
+ # Remove after migration to Rails 5
7
+ ActiveRecord::Base.raise_in_transactional_callbacks = true
8
+
9
+ ActiveRecord::Base.logger = Logger.new("log/test.log")
10
+
11
+ ActiveRecord::Schema.define do
12
+ self.verbose = false
13
+
14
+ execute "DROP SCHEMA public CASCADE;"
15
+ execute "CREATE SCHEMA public;"
16
+ execute "CREATE EXTENSION IF NOT EXISTS hstore;"
17
+
18
+ execute <<-SQL
19
+ DROP TYPE IF EXISTS access_level;
20
+ CREATE TYPE access_level AS ENUM (
21
+ 'private',
22
+ 'company',
23
+ 'viewable',
24
+ 'accessible'
25
+ );
26
+ SQL
27
+
28
+ create_table :users, force: true do |t|
29
+ t.integer :role_id
30
+ t.string :first_name
31
+ t.string :last_name
32
+ t.hstore :meta_data
33
+ t.column :access_level, :access_level
34
+ end
35
+ end
@@ -0,0 +1,14 @@
1
+ require "database_cleaner"
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:suite) do
5
+ DatabaseCleaner.strategy = :transaction
6
+ DatabaseCleaner.clean_with(:truncation)
7
+ end
8
+
9
+ config.around(:each) do |example|
10
+ DatabaseCleaner.cleaning do
11
+ example.run
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ require "factory_girl"
2
+
3
+ # factories are loaded from support/factories automatically
@@ -0,0 +1,13 @@
1
+ require "simplecov"
2
+
3
+ SimpleCov.start do
4
+ # add_filter do |src|
5
+ # # paths = %w(auth authentication authorization).map do |library_name|
6
+ # # File.expand_path("../../#{library_name}/lib", __FILE__)
7
+ # # end
8
+ #
9
+ # !paths.any? { |path| src.filename =~ /^#{Regexp.escape(path)}/ }
10
+ # end
11
+
12
+ minimum_coverage 96
13
+ end
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ role_id 123
4
+ first_name "Uni"
5
+ last_name "Corn"
6
+ access_level "viewable"
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ class User < ActiveRecord::Base
2
+ end
@@ -0,0 +1,89 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ GEM_ROOT = File.expand_path('../../', __FILE__)
5
+ GEM_SPEC = "simple-sql.gemspec"
6
+
7
+ require 'simple/sql/version'
8
+ VERSION_FILE_PATH = 'lib/simple/sql/version.rb'
9
+
10
+ class VersionNumberTracker
11
+ class << self
12
+ def update_version_file(old_version_number, new_version_number)
13
+ old_line = "VERSION = \"#{old_version_number}\""
14
+ new_line = "VERSION = \"#{new_version_number}\""
15
+ update = File.read(VERSION_FILE_PATH).gsub(old_line, new_line)
16
+ File.open(VERSION_FILE_PATH, 'w') { |file| file.puts update }
17
+ new_version_number
18
+ end
19
+
20
+ def auto_version_bump
21
+ old_version_number = Simple::SQL::VERSION
22
+ old = old_version_number.split('.')
23
+ current = old[0..-2] << old[-1].next
24
+ new_version_number = current.join('.')
25
+
26
+ update_version_file(old_version_number, new_version_number)
27
+ end
28
+
29
+ def manual_version_bump
30
+ update_version_file(Simple::SQL::VERSION, ENV['VERSION'])
31
+ end
32
+
33
+ def update_version_number
34
+ @version = ENV['VERSION'] ? manual_version_bump : auto_version_bump
35
+ end
36
+
37
+ attr_reader :version
38
+ end
39
+ end
40
+
41
+ namespace :release do
42
+ task :version do
43
+ VersionNumberTracker.update_version_number
44
+ end
45
+
46
+ task :build do
47
+ Dir.chdir(GEM_ROOT) do
48
+ sh("gem build #{GEM_SPEC}")
49
+ end
50
+ end
51
+
52
+ desc "Commit changes"
53
+ task :commit do
54
+ Dir.chdir(GEM_ROOT) do
55
+ version = VersionNumberTracker.version
56
+ sh("git add #{VERSION_FILE_PATH}")
57
+ sh("git commit -m \"bump auth to v#{version}\"")
58
+ sh("git tag -a v#{version} -m \"Tag\"")
59
+ end
60
+ end
61
+
62
+ desc "Push code and tags"
63
+ task :push do
64
+ sh('git push origin master')
65
+ sh('git push --tags')
66
+ end
67
+
68
+ desc "Cleanup"
69
+ task :clean do
70
+ Dir.glob(File.join(GEM_ROOT, '*.gem')).each { |f| FileUtils.rm_rf(f) }
71
+ end
72
+
73
+ desc "Push Gem to gemfury"
74
+ task :push_to_rubygems do
75
+ Dir.chdir(GEM_ROOT) { sh("gem push #{Dir.glob('*.gem').first}") }
76
+ end
77
+
78
+ task default: [
79
+ 'version',
80
+ 'clean',
81
+ 'build',
82
+ 'commit',
83
+ 'push',
84
+ 'push_to_rubygems'
85
+ ]
86
+ end
87
+
88
+ desc "Clean, build, commit and push"
89
+ task release: 'release:default'
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple-sql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - radiospiel
8
+ - mediapeers GmbH
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-01-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '4'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '4'
28
+ - !ruby/object:Gem::Dependency
29
+ name: pg_array_parser
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: pg
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '='
47
+ - !ruby/object:Gem::Version
48
+ version: '0.20'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '='
54
+ - !ruby/object:Gem::Version
55
+ version: '0.20'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '11'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '11'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '3.7'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '3.7'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rubocop
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: 0.52.1
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: 0.52.1
98
+ - !ruby/object:Gem::Dependency
99
+ name: factory_girl
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '='
103
+ - !ruby/object:Gem::Version
104
+ version: 4.8.0
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - '='
110
+ - !ruby/object:Gem::Version
111
+ version: 4.8.0
112
+ - !ruby/object:Gem::Dependency
113
+ name: database_cleaner
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '1'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '1'
126
+ - !ruby/object:Gem::Dependency
127
+ name: simplecov
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ description: SQL with a simple interface. Postgres only.
141
+ email: eno@radiospiel.org
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rubocop.yml"
148
+ - Gemfile
149
+ - Gemfile.lock
150
+ - README.md
151
+ - Rakefile
152
+ - bin/rake
153
+ - config/database.yml
154
+ - lib/simple/sql.rb
155
+ - lib/simple/sql/decoder.rb
156
+ - lib/simple/sql/encoder.rb
157
+ - lib/simple/sql/version.rb
158
+ - log/.gitkeep
159
+ - simple-sql.gemspec
160
+ - spec/simple/sql/version_spec.rb
161
+ - spec/simple/sql_ask_spec.rb
162
+ - spec/simple/sql_conversion_spec.rb
163
+ - spec/simple/sql_record_spec.rb
164
+ - spec/spec_helper.rb
165
+ - spec/support/001_database.rb
166
+ - spec/support/002_database_cleaner.rb
167
+ - spec/support/003_factories.rb
168
+ - spec/support/004_simplecov.rb
169
+ - spec/support/factories/user_factory.rb
170
+ - spec/support/model/user.rb
171
+ - tasks/release.rake
172
+ homepage: http://github.com/radiospiel/simple-sql
173
+ licenses: []
174
+ metadata: {}
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: '2.3'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubyforge_project:
191
+ rubygems_version: 2.5.1
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: SQL with a simple interface
195
+ test_files:
196
+ - spec/simple/sql/version_spec.rb
197
+ - spec/simple/sql_ask_spec.rb
198
+ - spec/simple/sql_conversion_spec.rb
199
+ - spec/simple/sql_record_spec.rb
200
+ - spec/spec_helper.rb
201
+ - spec/support/001_database.rb
202
+ - spec/support/002_database_cleaner.rb
203
+ - spec/support/003_factories.rb
204
+ - spec/support/004_simplecov.rb
205
+ - spec/support/factories/user_factory.rb
206
+ - spec/support/model/user.rb