simple-sql 0.2.0 → 0.2.1

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: 35cf2798d086495d8716eb22b6ce08bea72a5ab1
4
- data.tar.gz: 6e332f0a12d3442ee7a28e319cff5b8759a8affb
3
+ metadata.gz: 671eec226ce28823bcb0d2e749c3e11d7990ffca
4
+ data.tar.gz: f3447b03aa97ee19fd1e3fefd566ed24b77f2a85
5
5
  SHA512:
6
- metadata.gz: 45f2f76f763f1587b4e1d330e00f3d24e87c8f7dca32a08abc9fed7f1076f5d87d42cc1dc06eeb20a2ac2a5c92d3a8312a31642edcb80f7940bfb46cc02d5bb9
7
- data.tar.gz: d5efb732d0ebf9f8be40cf1dae1c3ba33782b4df0c92be284608fc74818b78b920177186da5fb5ef9ae6f704f11a49d2ae33cf8bd423df834b4a905bab694061
6
+ metadata.gz: 1cf66f360cb3d5cd721d6f978dcdc385e6fbfac3cf3aecd40083d8d5f9a9a1b849cf52f7884a29ece948ff4ca1164bf1ccabab97076ecbc789b6e45888bbefc4
7
+ data.tar.gz: d2dd45b2e4822fa591286030fd9bb874fd1a23fdc5ee93946c6859e64e4cf8413f4f880801baefdf86db96cf6b0a179221e1adb6b08f2f1be9d21f3afc8b6079
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- simple-sql (0.2.0)
4
+ simple-sql (0.2.1)
5
5
  pg (~> 0.20)
6
6
  pg_array_parser (~> 0)
7
7
 
@@ -1,17 +1,29 @@
1
1
  require_relative "sql/version.rb"
2
2
  require_relative "sql/decoder.rb"
3
3
  require_relative "sql/encoder.rb"
4
+ require_relative "sql/config.rb"
5
+ require_relative "sql/transactions.rb"
6
+ require_relative "sql/logging.rb"
7
+
8
+ require "logger"
4
9
 
5
10
  module Simple
6
11
  # The Simple::SQL module
7
12
  module SQL
8
13
  extend self
14
+ extend Transactions
15
+
16
+ attr_accessor :logger
17
+ self.logger = Logger.new(STDERR)
9
18
 
10
19
  # execute one or more sql statements. This method does not allow to pass in
11
20
  # arguments - since the pg client does not support this - but it allows to
12
21
  # run multiple sql statements separated by ";"
13
- extend Forwardable
14
- delegate exec: :pg
22
+ def exec(sql)
23
+ Logging.yield_logged sql do
24
+ connection.exec sql
25
+ end
26
+ end
15
27
 
16
28
  # Runs a query, with optional arguments, and returns the result. If the SQL
17
29
  # query returns rows with one column, this method returns an array of these
@@ -28,7 +40,7 @@ module Simple
28
40
  # end
29
41
 
30
42
  def all(sql, *args, &block)
31
- result = connection.exec_params(sql, Encoder.encode_args(args))
43
+ result = exec_logged(sql, *args)
32
44
  decoder = Decoder.new(result)
33
45
 
34
46
  enumerate(result, decoder, block)
@@ -62,7 +74,7 @@ module Simple
62
74
  # end
63
75
 
64
76
  def records(sql, *args, into: Hash, &block)
65
- result = connection.exec_params(sql, Encoder.encode_args(args))
77
+ result = exec_logged(sql, *args)
66
78
  decoder = Decoder.new(result, :record, into: into)
67
79
 
68
80
  enumerate(result, decoder, block)
@@ -78,6 +90,12 @@ module Simple
78
90
 
79
91
  private
80
92
 
93
+ def exec_logged(sql, *args)
94
+ Logging.yield_logged sql, *args do
95
+ connection.exec_params(sql, Encoder.encode_args(args))
96
+ end
97
+ end
98
+
81
99
  def enumerate(result, decoder, block)
82
100
  if block
83
101
  result.each_row do |row|
@@ -112,11 +130,8 @@ module Simple
112
130
  return ActiveRecord::Base.connection.raw_connection if defined?(ActiveRecord)
113
131
 
114
132
  STDERR.puts <<-SQL
115
- simple-sql works out of the box with ActiveRecord-based postgres connections.
116
-
117
- To use it without ActiveRecord you must connect to a database via
118
-
119
- Simple::SQL.connect!("postgresql://username:password@dbhost:port/dbname").
133
+ Simple::SQL works out of the box with ActiveRecord-based postgres connections, reusing the current connection.
134
+ To use without ActiveRecord you must connect to a database via Simple::SQL.connect!.
120
135
  SQL
121
136
 
122
137
  raise ArgumentError, "simple-sql: missing connection"
@@ -124,32 +139,19 @@ SQL
124
139
 
125
140
  public
126
141
 
127
- def connect!(url)
128
- require "pg"
142
+ # connects to the database specified via the url parameter. If called
143
+ # without argument it tries to determine a DATABASE_URL from either the
144
+ # environment setting (DATABASE_URL) or from a config/database.yml file,
145
+ # taking into account the RAILS_ENV and RACK_ENV settings.
146
+ def connect!(database_url = :auto)
147
+ database_url = Config.determine_url if database_url == :auto
148
+
149
+ logger.info "Connecting to #{database_url}"
150
+ config = Config.parse_url(database_url)
129
151
 
130
- config = db_config_from_url(url)
152
+ require "pg"
131
153
  connection = PG::Connection.new(config)
132
154
  self.connector = lambda { connection }
133
155
  end
134
-
135
- def db_config_from_url(url)
136
- require "uri"
137
-
138
- raise ArgumentError, "Invalid URL #{url.inspect}" unless url.is_a?(String)
139
- raise ArgumentError, "Invalid URL #{url.inspect}" unless url =~ /^postgres(ql)?s?:\/\//
140
-
141
- uri = URI.parse(url)
142
- raise ArgumentError, "Invalid URL #{url}" unless uri.hostname && uri.path
143
-
144
- config = {
145
- dbname: uri.path.sub(%r{^/}, ""),
146
- host: uri.hostname
147
- }
148
- config[:port] = uri.port if uri.port
149
- config[:user] = uri.user if uri.user
150
- config[:password] = uri.password if uri.password
151
- config[:sslmode] = uri.scheme == "postgress" || uri.scheme == "postgresqls" ? "require" : "prefer"
152
- config
153
- end
154
156
  end
155
157
  end
@@ -0,0 +1,52 @@
1
+ # private
2
+ module Simple::SQL::Config
3
+ extend self
4
+
5
+ # parse a DATABASE_URL, return PG::Connection settings.
6
+ def parse_url(url)
7
+ require "uri"
8
+
9
+ raise ArgumentError, "Invalid URL #{url.inspect}" unless url.is_a?(String)
10
+ raise ArgumentError, "Invalid URL #{url.inspect}" unless url =~ /^postgres(ql)?s?:\/\//
11
+
12
+ uri = URI.parse(url)
13
+ raise ArgumentError, "Invalid URL #{url}" unless uri.hostname && uri.path
14
+
15
+ config = {
16
+ dbname: uri.path.sub(%r{^/}, ""),
17
+ host: uri.hostname
18
+ }
19
+ config[:port] = uri.port if uri.port
20
+ config[:user] = uri.user if uri.user
21
+ config[:password] = uri.password if uri.password
22
+ config[:sslmode] = uri.scheme == "postgress" || uri.scheme == "postgresqls" ? "require" : "prefer"
23
+ config
24
+ end
25
+
26
+ # determines the database_url from either the DATABASE_URL environment setting
27
+ # or a config/database.yml file.
28
+ def determine_url
29
+ ENV["DATABASE_URL"] || database_url_from_database_yml
30
+ end
31
+
32
+ private
33
+
34
+ def database_url_from_database_yml
35
+ abc = read_database_yml
36
+ username, password, host, port, database, password = abc.values_at "username", "password", "host", "port", "database", "password"
37
+
38
+ username_and_password = [ username, password ].compact.join(":")
39
+ host_and_port = [ host, port ].compact.join(":")
40
+ "postgres://#{username_and_password}@#{host_and_port}/#{database}"
41
+ end
42
+
43
+ def read_database_yml
44
+ require "yaml"
45
+ database_config = YAML.load_file "config/database.yml"
46
+ env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
47
+
48
+ database_config[env] ||
49
+ database_config["defaults"] ||
50
+ raise("Invalid or missing database configuration in config/database.yml for #{env.inspect} environment")
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ module Simple
2
+ module SQL
3
+ module Logging
4
+ extend self
5
+
6
+ def yield_logged(sql, *args, &block)
7
+ r0 = Time.now
8
+ rv = yield
9
+ realtime = Time.now - r0
10
+ ::Simple::SQL.logger.debug "[sql] %.3f secs: %s" % [ realtime, format_query(sql, *args) ]
11
+ rv
12
+ rescue => e
13
+ realtime = Time.now - r0
14
+ ::Simple::SQL.logger.warn "[sql] %.3f secs: %s:\n\tfailed with error %s" % [ realtime, format_query(sql, *args), e.message ]
15
+ raise
16
+ end
17
+
18
+ private
19
+
20
+ def format_query(sql, *args)
21
+ sql = sql.gsub(/\s*\n\s*/, " ").gsub(/(\A\s+)|(\s+\z)/, "")
22
+ return sql if args.empty?
23
+ args = args.map(&:inspect).join(", ")
24
+ sql + " w/args: #{args}"
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,44 @@
1
+ # private
2
+ module Simple::SQL::Transactions
3
+ SELF = self
4
+
5
+ def self.nesting_level
6
+ Thread.current[:nesting_level] ||= 0
7
+ end
8
+
9
+ def self.nesting_level=(nesting_level)
10
+ Thread.current[:nesting_level] = nesting_level
11
+ end
12
+
13
+ def transaction(&block)
14
+ # Notes: by using "ensure" (as opposed to rescue) we are rolling back
15
+ # both when an exception was raised and when a value was thrown. This
16
+ # also means we have to track whether or not to rollback. i.e. do roll
17
+ # back when we yielded to &block but not otherwise.
18
+ #
19
+ # Also the transaction support is a bit limited: you cannot rollback.
20
+ # Rolling back from inside a nested transaction would require SAVEPOINT
21
+ # support; without the code is simpler at least :)
22
+ if SELF.nesting_level == 0
23
+ transaction_started = true
24
+ ask "BEGIN"
25
+ end
26
+
27
+ SELF.nesting_level += 1
28
+
29
+ return_value = yield
30
+
31
+ # Only commit if we started a transaction here.
32
+ if transaction_started
33
+ ask "COMMIT"
34
+ transaction_committed = true
35
+ end
36
+
37
+ return_value
38
+ ensure
39
+ SELF.nesting_level -= 1
40
+ if transaction_started && !transaction_committed
41
+ ask "ROLLBACK"
42
+ end
43
+ end
44
+ end
@@ -1,5 +1,5 @@
1
1
  module Simple
2
2
  module SQL
3
- VERSION = "0.2.0"
3
+ VERSION = "0.2.1"
4
4
  end
5
5
  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.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
@@ -166,8 +166,11 @@ files:
166
166
  - bin/rake
167
167
  - config/database.yml
168
168
  - lib/simple/sql.rb
169
+ - lib/simple/sql/config.rb
169
170
  - lib/simple/sql/decoder.rb
170
171
  - lib/simple/sql/encoder.rb
172
+ - lib/simple/sql/logging.rb
173
+ - lib/simple/sql/transactions.rb
171
174
  - lib/simple/sql/version.rb
172
175
  - log/.gitkeep
173
176
  - simple-sql.gemspec