simple-sql 0.2.5 → 0.2.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5cf881401ca767b7857ef63d6d04e9ce2e62185e
4
- data.tar.gz: 875aa241218c6bb2c0fd6a1b6c00d55f602d30f8
3
+ metadata.gz: 2fea0396d5b98a9ec21ea6910bf6b68bc35aab90
4
+ data.tar.gz: bdfd6c5e2a4abbbc82cc86b3564de1c5649d4333
5
5
  SHA512:
6
- metadata.gz: 64cf973e6c9878b5a55bb8fb7ffeb00e4fe888bd3dc59afa94ecf891243b99d90ef0f24a610406c2abcfc4f1fad6cfd5d2746380e4f7dd1d9e7de66f9f2725d9
7
- data.tar.gz: d5a1602a9ed6b52f3a291ffacc0a2cbad045757856e983d5c8bced572eae95a198f139779ecfa97a7dcc406cb09e7f644bca5a588849e76ca72a6edb7e63b668
6
+ metadata.gz: 50cd39402330417cbf4e6f81ff9ad49cc15b1bf96f3a11772e57e677803dac734e898d1b44036e299bda343a54fc9d6b606351377589ceb8908bd3d69280a046
7
+ data.tar.gz: bb6482542f96b64c8b9a75c19421dbfe01953fa6cdc76e2bdee5aaabcbdea931d44a8c2cf184819b6630559d7bc30757dbec5f0c5002b00df92a7ed93943eeb0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- simple-sql (0.2.5)
4
+ simple-sql (0.2.6)
5
5
  pg (~> 0.20)
6
6
  pg_array_parser (~> 0)
7
7
 
@@ -22,6 +22,7 @@ GEM
22
22
  tzinfo (~> 1.1)
23
23
  arel (6.0.4)
24
24
  ast (2.3.0)
25
+ awesome_print (0.4.0)
25
26
  builder (3.2.3)
26
27
  concurrent-ruby (1.0.5)
27
28
  database_cleaner (1.6.2)
@@ -78,6 +79,7 @@ PLATFORMS
78
79
 
79
80
  DEPENDENCIES
80
81
  activerecord (~> 4)
82
+ awesome_print (~> 0)
81
83
  database_cleaner (~> 1)
82
84
  factory_girl (= 4.8.0)
83
85
  pg (= 0.20)
data/bin/console ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ $: << "lib"
3
+ require "simple/sql"
4
+
5
+ SQL = Simple::SQL
6
+ SQL.connect!
7
+
8
+ def reload!
9
+ $VERBOSE = nil
10
+ Dir.glob("lib/simple/sql/**/*.rb").sort.each do |path|
11
+ STDERR.puts path
12
+ load path
13
+ end
14
+ end
15
+
16
+ require "irb"
17
+ IRB.start
data/config/database.yml CHANGED
@@ -1,7 +1,15 @@
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
1
+ defaults: &defaults
2
+ adapter: postgresql
3
+ encoding: utf8
4
+ host: '127.0.0.1'
5
+ username: postgres
6
+ pool: 5
7
+ timeout: 5000
8
+
9
+ test:
10
+ <<: *defaults
11
+ database: simple-sql-test
12
+
13
+ development:
14
+ <<: *defaults
15
+ database: simple-sql
data/lib/simple/sql.rb CHANGED
@@ -1,11 +1,14 @@
1
+ require "forwardable"
2
+ require "logger"
3
+
1
4
  require_relative "sql/version.rb"
2
5
  require_relative "sql/decoder.rb"
3
6
  require_relative "sql/encoder.rb"
4
7
  require_relative "sql/config.rb"
5
8
  require_relative "sql/logging.rb"
6
9
  require_relative "sql/connection.rb"
7
-
8
- require "logger"
10
+ require_relative "sql/reflection.rb"
11
+ require_relative "sql/insert.rb"
9
12
 
10
13
  module Simple
11
14
  # The Simple::SQL module
@@ -0,0 +1,65 @@
1
+ module Simple
2
+ module SQL
3
+ # Inse
4
+ def insert(table, records)
5
+ if records.is_a?(Hash)
6
+ return insert(table, [records]).first
7
+ end
8
+
9
+ return [] if records.empty?
10
+
11
+ inserter = Inserter.create(table_name: table.to_s, columns: records.first.keys)
12
+ inserter.insert(records: records)
13
+ end
14
+
15
+ class Inserter
16
+ SQL = ::Simple::SQL
17
+
18
+ @@inserters = {}
19
+
20
+ def self.create(table_name:, columns:)
21
+ @@inserters[[table_name, columns]] ||= new(table_name: table_name, columns: columns)
22
+ end
23
+
24
+ #
25
+ # - table_name - the name of the table
26
+ # - columns - name of columns, as Array[String] or Array[Symbol]
27
+ #
28
+ def initialize(table_name:, columns:)
29
+ @columns = columns
30
+
31
+ cols = []
32
+ vals = []
33
+
34
+ cols += columns
35
+ vals += columns.each_with_index.map { |_, idx| "$#{idx+1}" }
36
+
37
+ timestamp_columns = timestamp_columns_in_table(table_name) - columns.map(&:to_s)
38
+
39
+ cols += timestamp_columns
40
+ vals += timestamp_columns.map { "now()" }
41
+
42
+ @sql = "INSERT INTO #{table_name} (#{cols.join(",")}) VALUES(#{vals.join(",")}) RETURNING id"
43
+ end
44
+
45
+ # timestamp_columns are columns that will be set to the current time when
46
+ # inserting a record. This includes:
47
+ #
48
+ # - inserted_at (for Ecto)
49
+ # - created_at (for ActiveRecord)
50
+ # - updated_at (for Ecto and ActiveRecord)
51
+ def timestamp_columns_in_table(table_name)
52
+ columns_for_table = SQL::Reflection.columns(table_name).keys
53
+ columns_for_table & %w(inserted_at created_at updated_at)
54
+ end
55
+
56
+ def insert(records: records)
57
+ SQL.transaction do
58
+ records.map do |record|
59
+ SQL.ask @sql, *record.values_at(*@columns)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,58 @@
1
+ module Simple
2
+ module SQL
3
+ module Reflection
4
+ extend self
5
+
6
+ extend Forwardable
7
+ delegate [:ask, :all, :records, :record] => ::Simple::SQL
8
+
9
+ def tables(schema: "public")
10
+ if schema == "public"
11
+ sql = <<~SQL
12
+ SELECT table_name AS name, *
13
+ FROM information_schema.tables
14
+ WHERE table_schema=$1
15
+ SQL
16
+ else
17
+ sql = <<~SQL
18
+ SELECT table_schema || '.' || table_name AS name, *
19
+ FROM information_schema.tables
20
+ WHERE table_schema=$1
21
+ SQL
22
+ end
23
+ records = ::Simple::SQL.records sql, schema
24
+ records_by_attr(records, :name)
25
+ end
26
+
27
+ def columns(table_name)
28
+ schema, table_name = parse_table_name(table_name)
29
+ records = ::Simple::SQL.records <<~SQL, schema, table_name
30
+ SELECT
31
+ column_name AS name,
32
+ *
33
+ FROM information_schema.columns
34
+ WHERE table_schema=$1 AND table_name=$2
35
+ SQL
36
+
37
+ records_by_attr(records, :column_name)
38
+ end
39
+
40
+ private
41
+
42
+ def parse_table_name(table_name)
43
+ p1, p2 = table_name.split(".", 2)
44
+ if p2
45
+ [ p1, p2 ]
46
+ else
47
+ [ "public", p1 ]
48
+ end
49
+ end
50
+
51
+ def records_by_attr(records, attr)
52
+ records.inject({}) do |hsh, record|
53
+ hsh.update record[attr] => OpenStruct.new(record)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  module Simple
2
2
  module SQL
3
- VERSION = "0.2.5"
3
+ VERSION = "0.2.6"
4
4
  end
5
5
  end
data/simple-sql.gemspec CHANGED
@@ -41,4 +41,5 @@ Gem::Specification.new do |gem|
41
41
  gem.add_development_dependency 'factory_girl', '4.8.0'
42
42
  gem.add_development_dependency 'database_cleaner', '~> 1'
43
43
  gem.add_development_dependency 'simplecov', '~> 0'
44
+ gem.add_development_dependency 'awesome_print', '~> 0'
44
45
  end
@@ -1,10 +1,10 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Simple::SQL do
3
+ describe SQL do
4
4
  describe "VERSION" do
5
5
  it "defines a version string" do
6
6
  # Note: this allows for 0.12.34beta
7
- expect(Simple::SQL::VERSION).to match(/^\d+\.\d+\.\d+/)
7
+ expect(SQL::VERSION).to match(/^\d+\.\d+\.\d+/)
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ describe "Simple::SQL.ask" do
4
4
  USER_COUNT = 2
5
5
 
6
6
  def expects(expected_result, sql, *args)
7
- expect(Simple::SQL.ask(sql, *args)).to eq(expected_result)
7
+ expect(SQL.ask(sql, *args)).to eq(expected_result)
8
8
  end
9
9
 
10
10
  let!(:users) { 1.upto(USER_COUNT).map { create(:user) } }
@@ -15,5 +15,6 @@ describe "Simple::SQL.ask" do
15
15
  expects 2, "SELECT COUNT(*) FROM users"
16
16
  expects 1, "SELECT COUNT(*) FROM users WHERE id=$1", users.first.id
17
17
  expects 0, "SELECT COUNT(*) FROM users WHERE id=$1", -1
18
+ expects nil, "SELECT id FROM users WHERE FALSE"
18
19
  end
19
20
  end
@@ -0,0 +1,31 @@
1
+ require "spec_helper"
2
+
3
+ describe "Simple::SQL::Config" do
4
+ describe ".determine_url" do
5
+ it "reads config/database.yml" do
6
+ expect(SQL::Config.determine_url).to eq "postgres://postgres@127.0.0.1/simple-sql-test"
7
+ end
8
+ end
9
+
10
+ describe ".parse_url" do
11
+ it "parses a DATABASE_URL" do
12
+ parsed = SQL::Config.parse_url "postgres://foo:bar@server/database"
13
+ expect(parsed).to eq(
14
+ dbname: "database",
15
+ host: "server",
16
+ password: "bar",
17
+ sslmode: "prefer",
18
+ user: "foo")
19
+ end
20
+
21
+ it "may enforce SSL" do
22
+ parsed = SQL::Config.parse_url "postgress://foo:bar@server/database"
23
+ expect(parsed).to eq(
24
+ dbname: "database",
25
+ host: "server",
26
+ password: "bar",
27
+ sslmode: "require",
28
+ user: "foo")
29
+ end
30
+ end
31
+ end
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
 
3
3
  describe "Simple::SQL conversions" do
4
4
  def expects(expected_result, sql, *args)
5
- expect(Simple::SQL.ask(sql, *args)).to eq(expected_result)
5
+ expect(SQL.ask(sql, *args)).to eq(expected_result)
6
6
  end
7
7
 
8
8
  describe "data conversions" do
@@ -30,6 +30,19 @@ describe "Simple::SQL conversions" do
30
30
  expects({"a"=>1, "b"=>2}, 'SELECT \'{"a":1,"b":2}\'::jsonb')
31
31
  end
32
32
 
33
+ it "converts double precision" do
34
+ expects 1.0, "SELECT 1.0::double precision"
35
+ end
36
+
37
+ it "converts bool" do
38
+ expects true, "SELECT TRUE"
39
+ expects false, "SELECT FALSE"
40
+ end
41
+
42
+ it "converts hstore" do
43
+ expects({ a: "1", b: "3" }, "SELECT 'a=>1,b=>3'::hstore")
44
+ end
45
+
33
46
  xit "fails sometimes w/ malformed array literal, pt. 1" do
34
47
  expects 0, 'SELECT $1::varchar[]', [ "foo", 'foo,"bar}' ]
35
48
  end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+
3
+ describe "Simple::SQL.insert" do
4
+ USER_COUNT = 2
5
+ SQL = SQL
6
+
7
+ def expects(expected_result, sql, *args)
8
+ expect(SQL.record(sql, *args)).to eq(expected_result)
9
+ end
10
+
11
+ let!(:users) { 1.upto(USER_COUNT).map { create(:user) } }
12
+
13
+ it "inserts a single user" do
14
+ initial_ids = SQL.all("SELECT id FROM users")
15
+
16
+ id = SQL.insert :users, first_name: "foo", last_name: "bar"
17
+ expect(id).to be_a(Integer)
18
+ expect(initial_ids).not_to include(id)
19
+ expect(SQL.ask("SELECT count(*) FROM users")).to eq(USER_COUNT+1)
20
+
21
+ user = SQL.record("SELECT * FROM users WHERE id=$1", id, into: OpenStruct)
22
+ expect(user.first_name).to eq("foo")
23
+ expect(user.last_name).to eq("bar")
24
+ expect(user.created_at).to be_a(Time)
25
+
26
+ #
27
+ # r = SQL.record("SELECT COUNT(*) AS count FROM users")
28
+ # r = SQL.record("SELECT COUNT(*) AS count FROM users")
29
+ # expect(r).to eq({count: 2})
30
+ end
31
+ end
32
+
@@ -4,28 +4,29 @@ describe "Simple::SQL.record" do
4
4
  USER_COUNT = 2
5
5
 
6
6
  def expects(expected_result, sql, *args)
7
- expect(Simple::SQL.record(sql, *args)).to eq(expected_result)
7
+ expect(SQL.record(sql, *args)).to eq(expected_result)
8
8
  end
9
9
 
10
10
  let!(:users) { 1.upto(USER_COUNT).map { create(:user) } }
11
11
 
12
- before do
13
- expect(User.count).to eq(2)
14
- end
15
-
16
12
  it "calls the database" do
17
- r = Simple::SQL.record("SELECT COUNT(*) AS count FROM users")
13
+ r = SQL.record("SELECT COUNT(*) AS count FROM users")
18
14
  expect(r).to eq({count: 2})
19
15
  end
20
16
 
17
+ it "returns nil when there is no record" do
18
+ r = SQL.record("SELECT * FROM users WHERE FALSE", into: OpenStruct)
19
+ expect(r).to be_nil
20
+ end
21
+
21
22
  it "supports the into: option" do
22
- r = Simple::SQL.record("SELECT COUNT(*) AS count FROM users", into: OpenStruct)
23
+ r = SQL.record("SELECT COUNT(*) AS count FROM users", into: OpenStruct)
23
24
  expect(r).to be_a(OpenStruct)
24
25
  expect(r).to eq(OpenStruct.new(count: 2))
25
26
  end
26
27
 
27
28
  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
+ r = SQL.record("SELECT $1::integer AS count FROM users", 2, into: OpenStruct)
29
30
  expect(r).to be_a(OpenStruct)
30
31
  expect(r).to eq(OpenStruct.new(count: 2))
31
32
  end
@@ -0,0 +1,31 @@
1
+ require "spec_helper"
2
+
3
+ describe "Simple::SQL::Reflection" do
4
+ describe ".columns" do
5
+ it "returns the columns of a table in the public schema" do
6
+ r = SQL::Reflection.columns("users")
7
+ expect(r).to be_a(Hash)
8
+ expect(r["first_name"].name).to eq("first_name")
9
+ end
10
+
11
+ it "returns the columns of a table in a non-'public' schema" do
12
+ r = SQL::Reflection.columns("information_schema.tables")
13
+ expect(r["table_name"].name).to eq("table_name")
14
+ end
15
+ end
16
+
17
+ describe ".tables" do
18
+ it "returns the tables in the public schema" do
19
+ r = SQL::Reflection.tables
20
+ expect(r.keys).to include("users")
21
+ expect(r["users"].name).to eq("users")
22
+ end
23
+
24
+ it "returns tables in a non-'public' schema" do
25
+ r = SQL::Reflection.tables(schema: "information_schema")
26
+ expect(r.keys).to include("information_schema.tables")
27
+ expect(r["information_schema.tables"].name).to eq("information_schema.tables")
28
+ expect(r["information_schema.tables"].table_name).to eq("tables")
29
+ end
30
+ end
31
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,11 +7,13 @@ ENV["RACK_ENV"] = "test"
7
7
  ENV["RAILS_ENV"] = "test"
8
8
 
9
9
  require "rspec"
10
-
10
+ require "awesome_print"
11
11
  Dir.glob("./spec/support/**/*.rb").sort.each { |path| load path }
12
12
 
13
13
  require "simple/sql"
14
14
 
15
+ SQL = Simple::SQL
16
+
15
17
  RSpec.configure do |config|
16
18
  config.run_all_when_everything_filtered = true
17
19
  config.filter_run focus: (ENV["CI"] != "true")
@@ -1,7 +1,8 @@
1
1
  # connect to the database and setup the schema
2
2
  require "active_record"
3
-
4
- ActiveRecord::Base.establish_connection(YAML.load_file("config/database.yml"))
3
+ require "yaml"
4
+ abc = YAML.load_file("config/database.yml")
5
+ ActiveRecord::Base.establish_connection(abc["test"])
5
6
 
6
7
  # Remove after migration to Rails 5
7
8
  ActiveRecord::Base.raise_in_transactional_callbacks = true
@@ -31,5 +32,7 @@ ActiveRecord::Schema.define do
31
32
  t.string :last_name
32
33
  t.hstore :meta_data
33
34
  t.column :access_level, :access_level
35
+
36
+ t.timestamps
34
37
  end
35
38
  end
@@ -1,13 +1,10 @@
1
1
  require "simplecov"
2
2
 
3
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
4
+ # return true to remove src from coverage
5
+ add_filter do |src|
6
+ src.filename =~ /\/spec\//
7
+ end
11
8
 
12
9
  minimum_coverage 96
13
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-02-13 00:00:00.000000000 Z
12
+ date: 2018-02-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg_array_parser
@@ -151,6 +151,20 @@ dependencies:
151
151
  - - "~>"
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
+ - !ruby/object:Gem::Dependency
155
+ name: awesome_print
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - "~>"
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
154
168
  description: SQL with a simple interface. Postgres only.
155
169
  email: eno@radiospiel.org
156
170
  executables: []
@@ -163,6 +177,7 @@ files:
163
177
  - Gemfile.lock
164
178
  - README.md
165
179
  - Rakefile
180
+ - bin/console
166
181
  - bin/rake
167
182
  - config/database.yml
168
183
  - lib/simple-sql.rb
@@ -171,14 +186,19 @@ files:
171
186
  - lib/simple/sql/connection.rb
172
187
  - lib/simple/sql/decoder.rb
173
188
  - lib/simple/sql/encoder.rb
189
+ - lib/simple/sql/insert.rb
174
190
  - lib/simple/sql/logging.rb
191
+ - lib/simple/sql/reflection.rb
175
192
  - lib/simple/sql/version.rb
176
193
  - log/.gitkeep
177
194
  - simple-sql.gemspec
178
195
  - spec/simple/sql/version_spec.rb
179
196
  - spec/simple/sql_ask_spec.rb
197
+ - spec/simple/sql_config_spec.rb
180
198
  - spec/simple/sql_conversion_spec.rb
199
+ - spec/simple/sql_insert_spec.rb
181
200
  - spec/simple/sql_record_spec.rb
201
+ - spec/simple/sql_reflection_spec.rb
182
202
  - spec/spec_helper.rb
183
203
  - spec/support/001_database.rb
184
204
  - spec/support/002_database_cleaner.rb
@@ -213,8 +233,11 @@ summary: SQL with a simple interface
213
233
  test_files:
214
234
  - spec/simple/sql/version_spec.rb
215
235
  - spec/simple/sql_ask_spec.rb
236
+ - spec/simple/sql_config_spec.rb
216
237
  - spec/simple/sql_conversion_spec.rb
238
+ - spec/simple/sql_insert_spec.rb
217
239
  - spec/simple/sql_record_spec.rb
240
+ - spec/simple/sql_reflection_spec.rb
218
241
  - spec/spec_helper.rb
219
242
  - spec/support/001_database.rb
220
243
  - spec/support/002_database_cleaner.rb