simple-sql 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
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