yaasql 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dbeb6099694ca9dcf3eee459946df642f5d77759
4
+ data.tar.gz: c1be07baf1f630e5081f71f49832062cfeca9795
5
+ SHA512:
6
+ metadata.gz: 2d3c70104907a11301cb6d98e943a6003b01be0533dec70b0b511d08b8941f0f20261d73be1ff1eef7ac2bb97f5209da088bdf8aa7fda79c0a85ac948e4d7cc7
7
+ data.tar.gz: efeebac87fc68aa367909406464dc91ad5fa8a16c237a98720a69c91b05ea13999acbddc5311e4ce7c63cbeb5b09f842467a9331e9cfbbaf0aaea48f4bb74d44
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.14.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yaasql.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Horace Williams
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # Yaasql
2
+
3
+ Basic SQL-templating library inspired by [https://github.com/krisajenkins/yesql](https://github.com/krisajenkins/yesql).
4
+
5
+ You can read more about the rationale for a library like this in [Yesql's Readme](https://github.com/krisajenkins/yesql#rationale)
6
+
7
+ ## Usage
8
+
9
+ ### Installing
10
+
11
+ ```ruby
12
+ gem 'yaasql'
13
+ ```
14
+
15
+ ### Defining Queries
16
+
17
+ Create a `.sql` file to define your queries.
18
+
19
+ Queries **must** be identified with a comment line each query a name. This name will become the name of the Ruby method yaasql eventually creates for your query.
20
+
21
+ You may have multiple queries per file.
22
+
23
+ ```sql
24
+ -- name: count_examples
25
+ SELECT COUNT(*) FROM examples;
26
+ ```
27
+
28
+ ### Loading queries
29
+
30
+ ```ruby
31
+ require 'yaasql/db'
32
+ require 'pg'
33
+
34
+ class MyDB
35
+ DB_CONN = PG.connect(DB_URL)
36
+ extend Yaasql::DB
37
+ define_queries("./queries.sql", DB_CONN)
38
+ end
39
+
40
+ MyDB.new.count_examples
41
+ # => [{"count" => "3"}]
42
+ ```
43
+
44
+ ### Parameterizing Queries
45
+
46
+ Words with a leading `:` in query files will be interpreted as arguments to the query.
47
+
48
+ ```sql
49
+ -- name: get_example_by_id
50
+ SELECT * FROM examples where id = :id limit 1;
51
+
52
+ -- name: get_examples_by_id
53
+ SELECT * FROM examples where id =ANY(:ids);
54
+ ```
55
+
56
+ Arguments can then be provided as a symbol-keyed hash when querying the function:
57
+
58
+ ```ruby
59
+ db = MyDB.new
60
+
61
+ db.get_example_by_id({id: 1})
62
+ # => [{"id" => "1", "name" => "example 1"}]
63
+
64
+ db.get_examples_by_id({ids: [1,2,3]})
65
+ # => [{"id" => "1", "name" => "example 1"}, {"id" => "2", "name" => "example 2"}]
66
+ ```
67
+
68
+ ## Development
69
+
70
+ Requires a local postgres server for testing.
71
+
72
+ ```bash
73
+ bundle install
74
+ rake setup # creates the test postgres DB
75
+ rake # runs tests
76
+ ```
77
+
78
+ ### Releasing
79
+
80
+ * Builds the `.gem` package
81
+ * Creates and pushes a git tag for the current version
82
+ * Pushes the `.gem` to [rubygems.org](https://rubygems.org)
83
+
84
+ ```
85
+ bundle exec rake release
86
+ ```
87
+
88
+ ### Building a `.gem` package
89
+
90
+ ```
91
+ bundle exec rake build
92
+ ```
93
+
94
+ ### Installing Dev Version Locally
95
+
96
+ ```
97
+ bundle exec rake install
98
+ ```
99
+
100
+ #### Feature Wishlist
101
+
102
+ * [X] Reading multiple queries from single string (blank-line separated?)
103
+ * [X] Module for including into a namespace
104
+ * [X] Metaprogramming for defining query methods
105
+ * [X] Define queries from a file
106
+ * [X] Support array queries with `=ANY()`
107
+ * [ ]`where IN (...)` queries
108
+ * [ ] Option for stdout / stdin redirection (to support `COPY FROM` / `COPY TO` queries)
109
+ * [ ] Option for streaming queries to process results row-by-row (e.g. for large datasets)
110
+ * [ ] Support positional `?` arguments
111
+
112
+ ## License
113
+
114
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
115
+
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ TEST_DB = 'yaasql_test'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ end
11
+
12
+ desc "Setup the test DB"
13
+ task :setup do
14
+ `dropdb --if-exists #{TEST_DB}`
15
+ `createdb #{TEST_DB}`
16
+ `psql -d #{TEST_DB} -f ./test/schema.sql`
17
+ end
18
+
19
+ task :default => :test
data/lib/yaasql/db.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "./lib/yaasql/reader"
2
+
3
+ module Yaasql
4
+ module DB
5
+ def define_queries(file_path, db_conn)
6
+ queries = Reader.new.from_file(file_path)
7
+ queries.each do |q|
8
+ define_method(q.name) do |arguments = {}|
9
+ q.execute(db_conn, arguments)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,34 @@
1
+ require "./lib/yaasql/reader"
2
+
3
+ module Yaasql
4
+ class Query
5
+ attr_reader :name, :body, :arguments
6
+
7
+ def self.from_string(query)
8
+ new(Reader.new.components(query))
9
+ end
10
+
11
+ def initialize(components)
12
+ [:body, :name, :arguments].each do |param|
13
+ raise ArgumentError.new("Query missing component #{param}") unless components[param]
14
+ end
15
+ @body = components[:body]
16
+ @name = components[:name]
17
+ @arguments = components[:arguments]
18
+ end
19
+
20
+ def prepare(argument)
21
+ case argument
22
+ when Array
23
+ "{#{argument.join(",")}}"
24
+ else
25
+ argument
26
+ end
27
+ end
28
+
29
+ def execute(connection, args = {})
30
+ params = self.arguments.map { |a| prepare(args.fetch(a)) }
31
+ connection.exec_params(body, params).to_a
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ module Yaasql
2
+ class Reader
3
+ HEADER_PATTERN = /^-- name: (.*)\n/
4
+ def name(query)
5
+ match = query.match(HEADER_PATTERN)
6
+ if match
7
+ match[1].to_sym
8
+ else
9
+ raise ArgumentError.new("Must provide a header comment with query name")
10
+ end
11
+ end
12
+
13
+ def raw_body(query)
14
+ query.split("\n").map(&:strip).reject do |line|
15
+ line.start_with?('--')
16
+ end.join('\n')
17
+ end
18
+
19
+ def with_sql_args(body, arguments)
20
+ arguments.each.with_index.reduce(body) do |body, (arg, index)|
21
+ body.gsub(":#{arg}", "$#{index+1}")
22
+ end
23
+ end
24
+
25
+ def arguments(body)
26
+ body.scan(/:(\w+);?/).flatten.map(&:to_sym)
27
+ end
28
+
29
+ def components(query)
30
+ name = name(query)
31
+ body = raw_body(query)
32
+ arguments = arguments(body)
33
+ {name: name, body: with_sql_args(body, arguments), arguments: arguments}
34
+ end
35
+
36
+ def from_file(path)
37
+ query_strings = File.read(path).split("\n\n")
38
+ query_strings.map { |q| Query.new(components(q)) }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Yaasql
2
+ VERSION = "0.1.0"
3
+ end
data/lib/yaasql.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "yaasql/version"
2
+
3
+ module Yaasql
4
+ # Your code goes here...
5
+ end
data/yaassql.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'yaasql/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "yaasql"
8
+ spec.version = Yaasql::VERSION
9
+ spec.authors = ["Horace Williams"]
10
+ spec.email = ["horace@worace.works"]
11
+
12
+ spec.summary = "Simple SQL query-templating for ruby, a la https://github.com/krisajenkins/yesql."
13
+ spec.description = "Write db queries directly in SQL - no ORM or query-builder required. Include some conveniences around naming queries and providing arguments."
14
+ spec.homepage = "https://github.com/worace/yaasql"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.14"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "minitest", "~> 5.0"
25
+ spec.add_development_dependency "pg", "~> 0.18"
26
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yaasql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Horace Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pg
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.18'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.18'
69
+ description: Write db queries directly in SQL - no ORM or query-builder required.
70
+ Include some conveniences around naming queries and providing arguments.
71
+ email:
72
+ - horace@worace.works
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/yaasql.rb
84
+ - lib/yaasql/db.rb
85
+ - lib/yaasql/query.rb
86
+ - lib/yaasql/reader.rb
87
+ - lib/yaasql/version.rb
88
+ - yaassql.gemspec
89
+ homepage: https://github.com/worace/yaasql
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.5.1
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Simple SQL query-templating for ruby, a la https://github.com/krisajenkins/yesql.
113
+ test_files: []