db_mod 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +1 -3
- data/Guardfile +4 -0
- data/README.md +173 -2
- data/Rakefile +7 -1
- data/db_mod.gemspec +7 -2
- data/lib/db_mod.rb +35 -2
- data/lib/db_mod/create.rb +6 -3
- data/lib/db_mod/statements.rb +8 -0
- data/lib/db_mod/statements/configuration.rb +20 -3
- data/lib/db_mod/statements/configuration/as.rb +3 -1
- data/lib/db_mod/statements/configuration/defaults.rb +55 -8
- data/lib/db_mod/statements/configuration/method_configuration.rb +74 -1
- data/lib/db_mod/statements/configuration/returning.rb +31 -0
- data/lib/db_mod/statements/configuration/single.rb +3 -1
- data/lib/db_mod/statements/default_method_settings.rb +81 -0
- data/lib/db_mod/statements/parameters.rb +13 -0
- data/lib/db_mod/statements/prepared.rb +27 -8
- data/lib/db_mod/statements/statement.rb +10 -5
- data/lib/db_mod/transaction.rb +5 -0
- data/lib/db_mod/version.rb +1 -1
- data/spec/db_mod/statements/configuration/defaults_spec.rb +61 -0
- data/spec/db_mod/statements/configuration/method_configuration_spec.rb +41 -0
- data/spec/db_mod/statements/configuration/returning_spec.rb +69 -0
- data/spec/db_mod/statements/default_method_settings_spec.rb +63 -0
- data/spec/spec_helper.rb +8 -2
- metadata +83 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f28c5a3e9353c2ae963580ca872095598e926dd
|
4
|
+
data.tar.gz: 77b781ec8ba80b3d1adc89c6c515708f0ed5bda3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f722a8ef3716308bf5232811298b4dc72cbebcb487299859b62d7acf4416f171fea909cbd282ada834d440a0fd06d718c8e7808bb4b6f28c00991694fd0c5df8
|
7
|
+
data.tar.gz: 4fe5b2cd8272462a7f902de07cf4210ac38aa681cbc6223418d24b7371f1da1484f5d4fa3963fe30c9fa750dd32466dd871b59d151876c80690de9e21ebfe1ca
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
0.0.6 (2015-10-21)
|
2
|
+
==================
|
3
|
+
|
4
|
+
* Procs now allowed for default parameter values - [@dslh](https://github.com/dslh).
|
5
|
+
* Adds `returning` block to method configuration options - [@dslh](https://github.com/dslh).
|
6
|
+
* Adds module-level `default_method_settings` for default prepared and statement method
|
7
|
+
configuration - [@dslh](https://github.com/dslh).
|
8
|
+
* Fixes a bug with dynamic module method declarations - [@dslh](https://github.com/dslh).
|
9
|
+
|
1
10
|
0.0.5 (2015-10-19)
|
2
11
|
==================
|
3
12
|
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
##
|
1
|
+
## DbMod
|
2
2
|
|
3
3
|
Database enabled modules for ruby.
|
4
4
|
|
5
5
|
[![GitHub version](https://badge.fury.io/gh/dslh%2Fdb_mod.svg)](https://github.com/dslh/db_mod)
|
6
6
|
[![Travis CI](https://img.shields.io/travis/dslh/db_mod/master.svg)](https://travis-ci.org/dslh/db_mod)
|
7
|
+
[![Code Climate](https://codeclimate.com/github/dslh/db_mod/badges/gpa.svg)](https://codeclimate.com/github/dslh/db_mod)
|
8
|
+
[![Test Coverage](https://codeclimate.com/github/dslh/db_mod/badges/coverage.svg)](https://codeclimate.com/github/dslh/db_mod/coverage)
|
9
|
+
[![Inline docs](http://inch-ci.org/github/dslh/db_mod.svg?branch=master)](http://inch-ci.org/github/dslh/db_mod)
|
7
10
|
[![Gem downloads](https://img.shields.io/gem/dt/db_mod.svg)](https://rubygems.org/gems/db_mod)
|
8
11
|
|
9
12
|
[Rubydoc.info documentation](http://www.rubydoc.info/gems/db_mod)
|
@@ -20,6 +23,26 @@ guarantees will be made about backwards compatibility until v0.1.0.
|
|
20
23
|
|
21
24
|
Issues, feature or pull requests, comments and feedback all welcomed.
|
22
25
|
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
From the command line:
|
29
|
+
|
30
|
+
```
|
31
|
+
gem install db_mod
|
32
|
+
```
|
33
|
+
|
34
|
+
Or in your `Gemfile`:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
gem 'db_mod'
|
38
|
+
```
|
39
|
+
|
40
|
+
And then in your script:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
require 'db_mod'
|
44
|
+
```
|
45
|
+
|
23
46
|
## Usage
|
24
47
|
|
25
48
|
### The database connection
|
@@ -204,6 +227,10 @@ Statement and prepared methods can be configured on declaration by using
|
|
204
227
|
formatted as either a CSV document or an array of JSON objects, respectively.
|
205
228
|
|
206
229
|
```ruby
|
230
|
+
# db_mod makes no attempt to load these
|
231
|
+
require 'csv'
|
232
|
+
require 'json'
|
233
|
+
|
207
234
|
module Reports
|
208
235
|
include DbMod
|
209
236
|
|
@@ -281,5 +308,149 @@ def_statement(:a, %(
|
|
281
308
|
|
282
309
|
# ...
|
283
310
|
|
284
|
-
a
|
311
|
+
a # === a(4, 5, 6)
|
312
|
+
a(1) # === a(1, 5, 6)
|
313
|
+
a(1, 2) # === a(1, 2, 6)
|
314
|
+
a(1, 2, 3) # === a(1, 2, 3)
|
315
|
+
```
|
316
|
+
|
317
|
+
A proc may be given as the default value for any parameter.
|
318
|
+
The proc should accept one parameter, which will be the argument list/hash
|
319
|
+
(depending on if the statement uses numbered or named arguments)
|
320
|
+
and should return a single value to be used for this execution
|
321
|
+
of the query.
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
def_prepared(:default_min, %(
|
325
|
+
SELECT default_min
|
326
|
+
FROM defaults
|
327
|
+
WHERE foo_id = $1
|
328
|
+
)) { single(:value) }
|
329
|
+
|
330
|
+
def_prepared(:report, 'SELECT * FROM a WHERE b = $c AND d > $e') do
|
331
|
+
defaults min: ->(args) { default_min(args[:c]) }
|
332
|
+
as :json
|
333
|
+
end
|
334
|
+
```
|
335
|
+
|
336
|
+
The above example shows how the proc will be executed using the instance
|
337
|
+
of the `DbMod` module as scope, so statement, prepared, or other methods
|
338
|
+
may be accessed.
|
339
|
+
|
340
|
+
Note that the argument list will be partially constructed at the time it
|
341
|
+
is received by the proc; other default values may or may not yet have been
|
342
|
+
populated. Defaults will be populated in the order that they are declared
|
343
|
+
using `defaults`.
|
344
|
+
|
345
|
+
##### Custom method return values
|
346
|
+
|
347
|
+
Besides the built-in result transformations provided by `as` and `single`,
|
348
|
+
`db_mod` also allows arbitrary control over the return value of statement
|
349
|
+
and prepared methods using a block provided via `returning`:
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
def_prepared(:a, 'SELECT name, sound FROM animals') do
|
353
|
+
# Block parameter is the SQL result set
|
354
|
+
returning do |animals|
|
355
|
+
animals.map do |animal|
|
356
|
+
"the #{animal['name']} goes #{animal['sound']}"
|
357
|
+
end.join ' and '
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def_prepared(:important_report!, 'SELECT address FROM email WHERE id = $1') do
|
362
|
+
# 'single' and 'as' will transform the result set
|
363
|
+
# before it is passed to the block
|
364
|
+
single(:value)
|
365
|
+
|
366
|
+
returning { |email| send_email(email, a) }
|
367
|
+
end
|
368
|
+
|
369
|
+
# Block has instance-level scope
|
370
|
+
def send_email(address, body)
|
371
|
+
# ...
|
372
|
+
end
|
373
|
+
|
374
|
+
# ...
|
375
|
+
|
376
|
+
important_report!(123)
|
377
|
+
# === send_email('ex@mp.le', 'the sheep goes baa and the cow goes moo')
|
378
|
+
```
|
379
|
+
|
380
|
+
##### Default configuration settings
|
381
|
+
|
382
|
+
To save typing, modules may declare a `default_method_settings` block that will
|
383
|
+
be applied to all following `def_statement` and `def_prepared` definitions. The
|
384
|
+
dsl used is the same as for individual method configuration blocks.
|
385
|
+
|
386
|
+
```ruby
|
387
|
+
require 'csv'
|
388
|
+
|
389
|
+
module CsvReports
|
390
|
+
include DbMod
|
391
|
+
|
392
|
+
default_method_settings do
|
393
|
+
as(:csv).returning { |csv| send_report(csv) }
|
394
|
+
end
|
395
|
+
|
396
|
+
def send_report(csv)
|
397
|
+
# ...
|
398
|
+
end
|
399
|
+
|
400
|
+
def_prepared(:report_a, 'SELECT * FROM report_a WHERE user_id = $1')
|
401
|
+
def_prepared(:report_b, 'SELECT * FROM report_b WHERE user_id = $1')
|
402
|
+
def_prepared(:report_c, 'SELECT * FROM report_c WHERE user_id = $1')
|
403
|
+
end
|
404
|
+
|
405
|
+
module ReportEmailer
|
406
|
+
include CsvReports
|
407
|
+
|
408
|
+
default_method_settings do
|
409
|
+
single(:column)
|
410
|
+
returning do |ids|
|
411
|
+
ids.each { |id| send_all_reports_for(id) }
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def send_all_reports_for(user)
|
416
|
+
report_a(user)
|
417
|
+
report_b(user)
|
418
|
+
report_c(user)
|
419
|
+
end
|
420
|
+
|
421
|
+
def_statement(:send_all_reports, 'SELECT id FROM user')
|
422
|
+
def_statement(:send_priority_reports, 'SELECT id FROM user WHERE priority')
|
423
|
+
|
424
|
+
# individual settings may be overridden
|
425
|
+
def_statement(:send_all_a_reports, 'SELECT id FROM user') do
|
426
|
+
returning { |ids| ids.each { |id| report_a(id) } }
|
427
|
+
end
|
428
|
+
def_statement(:send_reports, 'SELECT id FROM user WHERE name = $name') do
|
429
|
+
single(:value).returning { |id| send_all_reports_for(id) }
|
430
|
+
end
|
431
|
+
end
|
432
|
+
```
|
433
|
+
|
434
|
+
Defaults don't cascade automatically from one module to another module that
|
435
|
+
has included it. However the following sorts of things work if you need more
|
436
|
+
flexibility in reusing default settings:
|
437
|
+
|
438
|
+
```ruby
|
439
|
+
|
440
|
+
BASE_SETTINGS = ->() { single(:row).as(:json) }
|
441
|
+
module A
|
442
|
+
include DbMod
|
443
|
+
|
444
|
+
# Use base settings with overrides in block
|
445
|
+
default_method_settings(BASE_SETTINGS) { as(:csv) }
|
446
|
+
end
|
447
|
+
|
448
|
+
A.default_method_settings # => { as: :csv, single: :row }
|
449
|
+
|
450
|
+
module B
|
451
|
+
include A
|
452
|
+
|
453
|
+
# Inherit settings from A, overrides as named args
|
454
|
+
default_method_settings(A.default_method_settings, as: :csv)
|
455
|
+
end
|
285
456
|
```
|
data/Rakefile
CHANGED
@@ -15,7 +15,7 @@ require 'rainbow/ext/string' unless String.respond_to?(:color)
|
|
15
15
|
require 'rubocop/rake_task'
|
16
16
|
RuboCop::RakeTask.new
|
17
17
|
|
18
|
-
task default: [:rubocop, :spec]
|
18
|
+
task default: [:rubocop, :inch, :spec]
|
19
19
|
|
20
20
|
require 'yard'
|
21
21
|
DOC_FILES = ['lib/**/*.rb', 'README.md']
|
@@ -23,3 +23,9 @@ DOC_FILES = ['lib/**/*.rb', 'README.md']
|
|
23
23
|
YARD::Rake::YardocTask.new(:doc) do |t|
|
24
24
|
t.files = DOC_FILES
|
25
25
|
end
|
26
|
+
|
27
|
+
require 'inch/rake'
|
28
|
+
Inch::Rake::Suggest.new do |inch|
|
29
|
+
inch.args << '--private'
|
30
|
+
inch.args << '--pedantic'
|
31
|
+
end
|
data/db_mod.gemspec
CHANGED
@@ -16,11 +16,16 @@ Gem::Specification.new do |s|
|
|
16
16
|
|
17
17
|
s.add_runtime_dependency 'pg'
|
18
18
|
|
19
|
-
s.add_development_dependency '
|
19
|
+
s.add_development_dependency 'bundler'
|
20
|
+
s.add_development_dependency 'codeclimate-test-reporter'
|
21
|
+
s.add_development_dependency 'inch'
|
22
|
+
s.add_development_dependency 'rake'
|
23
|
+
s.add_development_dependency 'redcarpet'
|
20
24
|
s.add_development_dependency 'rspec'
|
21
25
|
s.add_development_dependency 'rspec-mocks'
|
26
|
+
s.add_development_dependency 'rubocop'
|
27
|
+
s.add_development_dependency 'simplecov'
|
22
28
|
s.add_development_dependency 'yard'
|
23
|
-
s.add_development_dependency 'bundler'
|
24
29
|
|
25
30
|
s.files = `git ls-files`.split("\n")
|
26
31
|
s.test_files = `git ls-files -- spec/*`.split("\n")
|
data/lib/db_mod.rb
CHANGED
@@ -11,15 +11,32 @@ require_relative 'db_mod/version'
|
|
11
11
|
# will give your class or object the protected methods
|
12
12
|
# {#db_connect} and {#conn=}, allowing the connection
|
13
13
|
# to be set or created, as well as the methods {#conn},
|
14
|
-
# {#query}, {#transaction}, and
|
14
|
+
# {#query}, {#transaction}, and +def_prepared+.
|
15
15
|
module DbMod
|
16
16
|
include Transaction
|
17
17
|
|
18
18
|
# When a module includes {DbMod}, we define some
|
19
19
|
# class-level functions specific to the module.
|
20
|
+
# This technique is required where it is not
|
21
|
+
# sufficient to simply define a module method
|
22
|
+
# on {DbMod} itself due to metaprogramming techniques
|
23
|
+
# requiring access to the module as +self+.
|
24
|
+
#
|
25
|
+
# See {DbMod::Create.setup}
|
26
|
+
# and {DbMod::Statements.setup}
|
27
|
+
#
|
28
|
+
# @param mod [Module] module which has had {DbMod} included
|
29
|
+
# @see http://ruby-doc.org/core-2.2.3/Module.html#method-i-included
|
30
|
+
# Module#included
|
20
31
|
def self.included(mod)
|
21
32
|
DbMod::Create.setup(mod)
|
22
33
|
DbMod::Statements.setup(mod)
|
34
|
+
|
35
|
+
# Ensure that these definitions cascade when
|
36
|
+
# submodules are included in subsequent submodules.
|
37
|
+
class << mod
|
38
|
+
define_method(:included) { |sub_mod| DbMod.included(sub_mod) }
|
39
|
+
end
|
23
40
|
end
|
24
41
|
|
25
42
|
protected
|
@@ -27,9 +44,23 @@ module DbMod
|
|
27
44
|
# Database object to be used for all database
|
28
45
|
# interactions in this module.
|
29
46
|
# Use {#db_connect} to initialize the object.
|
30
|
-
|
47
|
+
#
|
48
|
+
# @return [PGconn] for now, only PostgreSQL is supported
|
49
|
+
attr_reader :conn
|
50
|
+
|
51
|
+
# A custom-built connection object
|
52
|
+
# may be supplied in place of calling {#db_connect}.
|
53
|
+
# Be aware in this case that certain responsibilities
|
54
|
+
# of {#db_connect} may need to be taken care of manually,
|
55
|
+
# in particular preparing SQL statements.
|
56
|
+
#
|
57
|
+
# @param value [PGconn] for now, only PostgreSQL is supported.
|
58
|
+
attr_writer :conn
|
31
59
|
|
32
60
|
# Shorthand for +conn.query+
|
61
|
+
#
|
62
|
+
# @param sql [String] SQL query to execute
|
63
|
+
# @return [Object] SQL result set
|
33
64
|
def query(sql)
|
34
65
|
unless @conn
|
35
66
|
fail DbMod::Exceptions::ConnectionNotSet, 'db_connect not called'
|
@@ -65,6 +96,7 @@ module DbMod
|
|
65
96
|
# Load any missing options from defaults
|
66
97
|
#
|
67
98
|
# @param options [Hash] see {#db_connect}
|
99
|
+
# @see #db_connect
|
68
100
|
def db_defaults!(options)
|
69
101
|
fail ArgumentError, 'database name :db not supplied' unless options[:db]
|
70
102
|
options[:port] ||= 5432
|
@@ -75,6 +107,7 @@ module DbMod
|
|
75
107
|
# Create the database object itself.
|
76
108
|
#
|
77
109
|
# @param options [Hash] see {#db_connect}
|
110
|
+
# @return [PGconn] a new database connection
|
78
111
|
def db_connect!(options)
|
79
112
|
PGconn.connect(
|
80
113
|
options[:host],
|
data/lib/db_mod/create.rb
CHANGED
@@ -16,9 +16,11 @@ module DbMod
|
|
16
16
|
# for a module that has just had {DbMod}
|
17
17
|
# included.
|
18
18
|
#
|
19
|
-
# @param mod [Module]
|
19
|
+
# @param mod [Module] the module where {DbMod}
|
20
|
+
# has been included
|
21
|
+
# @see DbMod.included
|
20
22
|
def self.setup(mod)
|
21
|
-
|
23
|
+
class << mod
|
22
24
|
define_method(:create) do |options = {}|
|
23
25
|
@instantiable_class ||= Create.instantiable_class(self)
|
24
26
|
|
@@ -33,7 +35,8 @@ module DbMod
|
|
33
35
|
# and can be instantiated with either a connection object
|
34
36
|
# or some connection options.
|
35
37
|
#
|
36
|
-
# @param mod [Module]
|
38
|
+
# @param mod [Module] the module to build a class for
|
39
|
+
# @return [Class] a singleton instantiable class that includes +mod+
|
37
40
|
def self.instantiable_class(mod)
|
38
41
|
Class.new do
|
39
42
|
include mod
|
data/lib/db_mod/statements.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'statements/configuration'
|
2
|
+
require_relative 'statements/default_method_settings'
|
2
3
|
require_relative 'statements/statement'
|
3
4
|
require_relative 'statements/prepared'
|
4
5
|
|
@@ -12,7 +13,14 @@ module DbMod
|
|
12
13
|
module Statements
|
13
14
|
# Called when a module includes {DbMod},
|
14
15
|
# defines module-level +def_statement+ and +def_prepared+ dsl methods.
|
16
|
+
#
|
17
|
+
# @param mod [Module] module that has had {DbMod} included
|
18
|
+
# @see DbMod.included
|
19
|
+
# @see DefaultMethodSettings
|
20
|
+
# @see Prepared
|
21
|
+
# @see Statement
|
15
22
|
def self.setup(mod)
|
23
|
+
DbMod::Statements::DefaultMethodSettings.setup(mod)
|
16
24
|
DbMod::Statements::Prepared.setup(mod)
|
17
25
|
DbMod::Statements::Statement.setup(mod)
|
18
26
|
end
|
@@ -21,7 +21,14 @@ module DbMod
|
|
21
21
|
# @yield dsl block may be passed, which will be evaluated using a
|
22
22
|
# {MethodConfiguration} object as scope
|
23
23
|
def self.def_configurable(mod, name, definition, params = 0, &block)
|
24
|
-
config =
|
24
|
+
config =
|
25
|
+
if block_given?
|
26
|
+
MethodConfiguration.new(mod.default_method_settings, &block)
|
27
|
+
else
|
28
|
+
mod.default_method_settings
|
29
|
+
end
|
30
|
+
|
31
|
+
config &&= config.to_hash
|
25
32
|
|
26
33
|
definition = attach_result_processors(definition, config) if config
|
27
34
|
definition = attach_param_processor(definition, params, config)
|
@@ -45,6 +52,7 @@ module DbMod
|
|
45
52
|
|
46
53
|
elsif params.is_a?(Fixnum) && params > 0
|
47
54
|
define_fixed_args_method(definition, params)
|
55
|
+
|
48
56
|
else
|
49
57
|
->() { instance_exec(&definition) }
|
50
58
|
end
|
@@ -92,9 +100,11 @@ module DbMod
|
|
92
100
|
# @param definition [Proc] base method definition
|
93
101
|
# @param config [MethodConfiguration] configuration declared at
|
94
102
|
# method definition time
|
103
|
+
# @return [Proc] extended method definition
|
95
104
|
def self.attach_result_processors(definition, config)
|
96
105
|
definition = Single.extend(definition, config)
|
97
106
|
definition = As.extend(definition, config)
|
107
|
+
definition = Returning.extend(definition, config)
|
98
108
|
|
99
109
|
definition
|
100
110
|
end
|
@@ -108,9 +118,16 @@ module DbMod
|
|
108
118
|
# processing), perform some transform on it and return the result.
|
109
119
|
#
|
110
120
|
# @param definition [Proc] base method definition
|
111
|
-
# @param processor [
|
121
|
+
# @param processor [Proc,#call] result processor
|
122
|
+
# @return [Proc] wrapped method definition
|
112
123
|
def self.attach_result_processor(definition, processor)
|
113
|
-
|
124
|
+
if processor.is_a? Proc
|
125
|
+
lambda do |*args|
|
126
|
+
instance_exec(instance_exec(*args, &definition), &processor)
|
127
|
+
end
|
128
|
+
else
|
129
|
+
->(*args) { processor.call instance_exec(*args, &definition) }
|
130
|
+
end
|
114
131
|
end
|
115
132
|
end
|
116
133
|
end
|