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 +7 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +35 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +90 -0
- data/README.md +107 -0
- data/Rakefile +9 -0
- data/bin/rake +16 -0
- data/config/database.yml +7 -0
- data/lib/simple/sql.rb +103 -0
- data/lib/simple/sql/decoder.rb +114 -0
- data/lib/simple/sql/encoder.rb +21 -0
- data/lib/simple/sql/version.rb +5 -0
- data/log/.gitkeep +0 -0
- data/simple-sql.gemspec +43 -0
- data/spec/simple/sql/version_spec.rb +10 -0
- data/spec/simple/sql_ask_spec.rb +19 -0
- data/spec/simple/sql_conversion_spec.rb +41 -0
- data/spec/simple/sql_record_spec.rb +32 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/001_database.rb +35 -0
- data/spec/support/002_database_cleaner.rb +14 -0
- data/spec/support/003_factories.rb +3 -0
- data/spec/support/004_simplecov.rb +13 -0
- data/spec/support/factories/user_factory.rb +8 -0
- data/spec/support/model/user.rb +2 -0
- data/tasks/release.rake +89 -0
- metadata +206 -0
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
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
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
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")
|
data/config/database.yml
ADDED
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
|
data/log/.gitkeep
ADDED
File without changes
|
data/simple-sql.gemspec
ADDED
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|
data/tasks/release.rake
ADDED
@@ -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
|