hsql 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +14 -0
- data/.travis.yml +2 -0
- data/README.md +15 -13
- data/Rakefile +6 -2
- data/bin/hsql +51 -13
- data/examples/date_math.sql +69 -0
- data/hsql.gemspec +3 -1
- data/lib/hsql/data.rb +95 -0
- data/lib/hsql/file.rb +51 -15
- data/lib/hsql/query.rb +1 -1
- data/lib/hsql/template.rb +11 -2
- data/lib/hsql/version.rb +2 -1
- data/lib/hsql.rb +0 -19
- metadata +34 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad536a4589ef6fd47d79acfbbdefe48e99515cb5
|
4
|
+
data.tar.gz: d8d754d2e5e6284be8273edb111ebeeee17ad402
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e81cd7ba8c73332637181929b64e50d91398402bd391e22fba68afd12f0fa662a128dcb34de0ec26945b6384e02be7f7851fbe967926389dd3adb16358ada34
|
7
|
+
data.tar.gz: 08f0d9f2a3a1ec99f99e6cfc32c53fd0be8710075f21b3d227d99fb57dfc8f4a5b50270fda18167e6865bdbe3fd4dd56029a735b99ca885eb17f45ff063d0c6b
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
Style/TrailingComma:
|
3
|
+
EnforcedStyleForMultiline: comma
|
4
|
+
|
5
|
+
Metrics/LineLength:
|
6
|
+
Max: 110
|
7
|
+
|
8
|
+
StructInheritance:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
# `module_function` is strictly worse than `extend self` due to the confusion
|
12
|
+
# `module_function` introduces by not copying private methods to the class.
|
13
|
+
Style/ModuleFunction:
|
14
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -13,19 +13,21 @@ Rather than specifying variables and metadata for a set of database
|
|
13
13
|
queries in a `.rb`,`.py` or other programming language source file the queries
|
14
14
|
should be written to a .sql file directly.
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
16
|
+
```sql
|
17
|
+
# filename: daily_summary.sql
|
18
|
+
owner: jackdanger
|
19
|
+
schedule: hourly
|
20
|
+
data:
|
21
|
+
production:
|
22
|
+
output_table: summaries
|
23
|
+
update_condition:
|
24
|
+
development:
|
25
|
+
output_table: jackdanger_summaries
|
26
|
+
update_condition: WHERE 1 <> 1
|
27
|
+
---
|
28
|
+
INSERT INTO {{{output_table}}} SELECT * FROM interesting_information;
|
29
|
+
UPDATE summaries_performed SET complete = 1 {{{update_condition}}};
|
30
|
+
```
|
29
31
|
|
30
32
|
The above is a SQL file and any text editor will allow analysts to use
|
31
33
|
code completion and syntax highlighting for their queries.
|
data/Rakefile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
3
4
|
|
4
|
-
RSpec::Core::RakeTask.new
|
5
|
+
RSpec::Core::RakeTask.new
|
6
|
+
RuboCop::RakeTask.new
|
5
7
|
|
6
|
-
task default: :spec
|
8
|
+
task default: [:lint, :spec]
|
9
|
+
task test: :spec
|
10
|
+
task lint: :rubocop
|
data/bin/hsql
CHANGED
@@ -3,34 +3,72 @@
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'yaml'
|
5
5
|
require 'json'
|
6
|
+
require 'active_support/time'
|
6
7
|
|
7
8
|
options = {}
|
8
9
|
option_parser = OptionParser.new do |opts|
|
9
10
|
opts.banner = <<-BANNER
|
10
|
-
|
11
|
+
Usage: #{__FILE__} file [environment] [--yaml]
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
--yaml: Outputs only the YAML metadata from the file, not the SQL queries themselves.
|
13
|
+
ARGUMENTS
|
14
|
+
file: Any *.sql file that has a YAML header ending in three hyphens.
|
15
15
|
|
16
|
-
|
17
|
-
open https://github.com/JackDanger/hsql
|
16
|
+
OPTIONS
|
18
17
|
BANNER
|
19
18
|
|
20
|
-
opts.on('-
|
19
|
+
opts.on('-d DATE',
|
20
|
+
'--date DATE',
|
21
|
+
'--timestamp DATE', 'The time that the SQL will consider to be "now".') do |option|
|
22
|
+
options[:timestamp] = Time.parse(option) if option
|
23
|
+
end
|
24
|
+
opts.on('-e ENV',
|
25
|
+
'--env ENV',
|
26
|
+
'Which key of the YAML header "data:" key you want to interpolate.') do |option|
|
27
|
+
options[:environment] = option
|
28
|
+
end
|
29
|
+
opts.on('-y', '--yaml', 'Output just the metadata for this file as YAML') do |_option|
|
21
30
|
options[:meta_only] = 'yaml'
|
22
31
|
end
|
23
|
-
opts.on('-j', '--json', 'Output metadata for this file as JSON') do |
|
32
|
+
opts.on('-j', '--json', 'Output just the metadata for this file as JSON') do |_option|
|
24
33
|
options[:meta_only] = 'json'
|
25
34
|
end
|
26
|
-
|
35
|
+
opts.on_tail('-v', '--version', 'Show the current version of HSQL') do |_option|
|
36
|
+
puts HSQL::VERSION
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
opts.on('-h', '--help', 'Show the full help documentation') do |_option|
|
40
|
+
require_relative '../lib/hsql/data'
|
41
|
+
<<-HELP
|
42
|
+
TEMPLATE
|
43
|
+
|
44
|
+
You can use the Mustache syntax (three curly braces) to interpolate any of your
|
45
|
+
'data' into your SQL. You specify the data in the YAML header like so:
|
46
|
+
|
47
|
+
data:
|
48
|
+
production: # whatever you want to be interpolated into your SQL
|
49
|
+
name: Alison # when you pass 'production' as the environment argument
|
50
|
+
development:
|
51
|
+
name: Kailey
|
52
|
+
---
|
53
|
+
SELECT * FROM users WHERE name = '{{{name}}}'
|
27
54
|
|
55
|
+
|
56
|
+
There are some common date values available to you at all times, without having
|
57
|
+
to be defined in the YAML header:
|
58
|
+
|
59
|
+
#{HSQL::Data.for_humans}
|
60
|
+
|
61
|
+
For more details, run:
|
62
|
+
open https://github.com/JackDanger/hsql
|
63
|
+
HELP
|
64
|
+
end
|
65
|
+
end
|
28
66
|
option_parser.parse!
|
29
67
|
|
30
|
-
filename
|
68
|
+
filename = ARGV.first
|
31
69
|
|
32
|
-
unless filename
|
33
|
-
puts option_parser
|
70
|
+
unless filename
|
71
|
+
puts option_parser
|
34
72
|
exit 1
|
35
73
|
end
|
36
74
|
|
@@ -43,7 +81,7 @@ end
|
|
43
81
|
|
44
82
|
require_relative '../lib/hsql'
|
45
83
|
|
46
|
-
file = HSQL.parse_file(file,
|
84
|
+
file = HSQL::File.parse_file(file, options)
|
47
85
|
if 'yaml' == options[:meta_only]
|
48
86
|
puts file.metadata.to_yaml
|
49
87
|
elsif 'json' == options[:meta_only]
|
@@ -0,0 +1,69 @@
|
|
1
|
+
/*
|
2
|
+
Demonstrating use of the builtin time and date variables.
|
3
|
+
No YAML front matter was necessary for this file to work.
|
4
|
+
|
5
|
+
To see all possible variables you can use run:
|
6
|
+
|
7
|
+
$ hsql --help
|
8
|
+
|
9
|
+
To see yourself how this file works run:
|
10
|
+
|
11
|
+
$ hsql examples/date_math.sql
|
12
|
+
|
13
|
+
To change the values of the times just pass a --date or --timestamp argument:
|
14
|
+
|
15
|
+
$ hsql examples/date_math.sql --timestamp '1986-01-12 17:12:00'
|
16
|
+
|
17
|
+
*/
|
18
|
+
|
19
|
+
SELECT * FROM users WHERE created_at = {{{now}}};
|
20
|
+
/* Becomes:
|
21
|
+
SELECT * FROM users WHERE created_at = '2015-08-19 21:13:82 -0700' */
|
22
|
+
|
23
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_hour}}} AND {{{end_of_hour}}};
|
24
|
+
/* Becomes:
|
25
|
+
SELECT * FROM users WHERE created_at >= '2015-08-19 21:00:00 -0700' AND created_at <= '2015-08-19 21:59:59 -0700' */
|
26
|
+
|
27
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_previous_hour}}} AND {{{end_of_previous_hour}}};
|
28
|
+
/* Becomes:
|
29
|
+
SELECT * FROM users WHERE created_at >= '2015-08-19 20:00:00 -0700' AND created_at <= '2015-08-19 20:59:59 -0700' */
|
30
|
+
|
31
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_day}}} AND {{{end_of_day}}};
|
32
|
+
/* Becomes:
|
33
|
+
SELECT * FROM users WHERE created_at >= '2015-08-19 00:00:00 -0700' AND created_at <= '2015-08-19 23:59:59 -0700' */
|
34
|
+
|
35
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_previous_day}}} AND {{{end_of_previous_day}}};
|
36
|
+
/* Becomes:
|
37
|
+
SELECT * FROM users WHERE created_at >= '2015-08-18 00:00:00 -0700' AND created_at <= '2015-08-18 23:59:59 -0700' */
|
38
|
+
|
39
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_week}}} AND {{{end_of_week}}};
|
40
|
+
/* Becomes:
|
41
|
+
SELECT * FROM users WHERE created_at >= '2015-08-17 00:00:00 -0700' AND created_at <= '2015-08-23 23:59:59 -0700' */
|
42
|
+
|
43
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_previous_week}}} AND {{{end_of_previous_week}}};
|
44
|
+
/* Becomes:
|
45
|
+
SELECT * FROM users WHERE created_at >= '2015-08-10 00:00:00 -0700' AND created_at <= '2015-08-16 23:59:59 -0700' */
|
46
|
+
|
47
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_month}}} AND {{{end_of_month}}};
|
48
|
+
/* Becomes:
|
49
|
+
SELECT * FROM users WHERE created_at >= '2015-08-01 00:00:00 -0700' AND created_at <= '2015-08-31 23:59:59 -0700' */
|
50
|
+
|
51
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_previous_month}}} AND {{{end_of_previous_month}}};
|
52
|
+
/* Becomes:
|
53
|
+
SELECT * FROM users WHERE created_at >= '2015-07-01 00:00:00 -0700' AND created_at <= '2015-07-31 23:59:59 -0700' */
|
54
|
+
|
55
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_quarter}}} AND {{{end_of_quarter}}};
|
56
|
+
/* Becomes:
|
57
|
+
SELECT * FROM users WHERE created_at >= '2015-07-01 00:00:00 -0700' AND created_at <= '2015-09-30 23:59:59 -0700' */
|
58
|
+
|
59
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_previous_quarter}}} AND {{{end_of_previous_quarter}}};
|
60
|
+
/* Becomes:
|
61
|
+
SELECT * FROM users WHERE created_at >= '2015-04-01 00:00:00 -0700' AND created_at <= '2015-06-30 23:59:59 -0700' */
|
62
|
+
|
63
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_year}}} AND {{{end_of_year}}};
|
64
|
+
/* Becomes:
|
65
|
+
SELECT * FROM users WHERE created_at >= '2015-01-01 00:00:00 -0800' AND created_at <= '2015-12-31 23:59:59 -0800' */
|
66
|
+
|
67
|
+
SELECT * FROM users WHERE created_at BETWEEN {{{beginning_of_previous_year}}} AND {{{end_of_previous_year}}};
|
68
|
+
/* Becomes:
|
69
|
+
SELECT * FROM users WHERE created_at >= '2014-01-01 00:00:00 -0800' AND created_at <= '2014-12-31 23:59:59 -0800' */
|
data/hsql.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['gems@jackcanty.com']
|
11
11
|
|
12
12
|
spec.summary = 'Store a hash of data with your SQL queries.'
|
13
|
-
spec.description = 'Write SQL queries
|
13
|
+
spec.description = 'Write SQL queries with Mustache and easily render them for specific dates/times'
|
14
14
|
spec.homepage = 'https://github.com/JackDanger/hsql'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
@@ -20,10 +20,12 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
22
|
spec.add_dependency 'mustache', '> 0'
|
23
|
+
spec.add_dependency 'activesupport', '> 0'
|
23
24
|
# spec.add_dependency 'pg_query', '>= 0.6.2'
|
24
25
|
spec.add_development_dependency 'bundler', '~> 1.10'
|
25
26
|
spec.add_development_dependency 'rubocop', '~> 0.3'
|
26
27
|
spec.add_development_dependency 'rake', '> 0'
|
27
28
|
spec.add_development_dependency 'rspec', '~> 3.3'
|
29
|
+
spec.add_development_dependency 'timecop', '~> 0.8.0'
|
28
30
|
spec.add_development_dependency 'pry-byebug', '> 3'
|
29
31
|
end
|
data/lib/hsql/data.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'active_support/time'
|
2
|
+
module HSQL
|
3
|
+
# This module provides a set of useful values to be inserted into templates.
|
4
|
+
module Data
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# The internal API for reading this data and inserting it into queries.
|
8
|
+
def for_machines(time)
|
9
|
+
Hash[
|
10
|
+
calendar_moments(time).map { |name, value, _documentation| [name, "'#{value}'"] }
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
# For presenting to users the possible template variables they can use.
|
15
|
+
def for_humans
|
16
|
+
values = calendar_moments(Time.current)
|
17
|
+
for_screen = values.map do |name, _, description|
|
18
|
+
["{{{#{name}}}}", description]
|
19
|
+
end
|
20
|
+
width = for_screen.max_by { |name, _| name.size }.first.size
|
21
|
+
|
22
|
+
for_screen.map do |name, documentation|
|
23
|
+
" #{name.ljust(width, ' ')} #{documentation}"
|
24
|
+
end.join("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# A set of template variables that are always available and defined in
|
30
|
+
# relation to the provided time or date.
|
31
|
+
# The structure is designed to make generating documentation easier.
|
32
|
+
#
|
33
|
+
# rubocop:disable Metrics/LineLength, Metrics/MethodLength, Metrics/AbcSize
|
34
|
+
def calendar_moments(time)
|
35
|
+
# Time ranges surrounding the moment provided.
|
36
|
+
moment = time
|
37
|
+
moments = []
|
38
|
+
|
39
|
+
moments += [
|
40
|
+
['now', moment, 'The moment provided via --date or --timestamp (defaults to the current system time)'],
|
41
|
+
]
|
42
|
+
|
43
|
+
moments += [
|
44
|
+
['beginning_of_hour', moment.beginning_of_hour, 'The first second of the hour'],
|
45
|
+
['beginning_of_day', moment.beginning_of_day, 'The first second of the day'],
|
46
|
+
['beginning_of_week', moment.beginning_of_week, 'The first second of the week'],
|
47
|
+
['beginning_of_month', moment.beginning_of_month, 'The first second of the month'],
|
48
|
+
['beginning_of_quarter', moment.beginning_of_quarter, 'The first second of the quarter'],
|
49
|
+
['beginning_of_year', moment.beginning_of_year, 'The first second of the year'],
|
50
|
+
['end_of_hour', moment.end_of_hour, 'The last second of the hour'],
|
51
|
+
['end_of_day', moment.end_of_day, 'The last second of the day'],
|
52
|
+
['end_of_week', moment.end_of_week, 'The last second of the week'],
|
53
|
+
['end_of_month', moment.end_of_month, 'The last second of the month'],
|
54
|
+
['end_of_quarter', moment.end_of_quarter, 'The last second of the quarter'],
|
55
|
+
['end_of_year', moment.end_of_year, 'The last second of the year'],
|
56
|
+
]
|
57
|
+
|
58
|
+
# Time ranges immediately preceding the one in which `time` appears.
|
59
|
+
moment = time - 1.hour
|
60
|
+
moments += [
|
61
|
+
['beginning_of_previous_hour', moment.beginning_of_hour, 'The first second of the previous hour'],
|
62
|
+
['end_of_previous_hour', moment.end_of_hour, 'The last second of the previous hour'],
|
63
|
+
]
|
64
|
+
|
65
|
+
moment = time - 1.day
|
66
|
+
moments += [
|
67
|
+
['beginning_of_previous_day', moment.beginning_of_day, 'The first second of the previous day'],
|
68
|
+
['end_of_previous_day', moment.end_of_day, 'The last second of the previous day'],
|
69
|
+
]
|
70
|
+
|
71
|
+
moment = time - 1.week
|
72
|
+
moments += [
|
73
|
+
['beginning_of_previous_week', moment.beginning_of_week, 'The first second of the previous week'],
|
74
|
+
['end_of_previous_week', moment.end_of_week, 'The last second of the previous week'],
|
75
|
+
]
|
76
|
+
moment = time - 1.month
|
77
|
+
moments += [
|
78
|
+
['beginning_of_previous_month', moment.beginning_of_month, 'The first second of the previous month'],
|
79
|
+
['end_of_previous_month', moment.end_of_month, 'The last second of the previous month'],
|
80
|
+
]
|
81
|
+
moment = time - 3.months
|
82
|
+
moments += [
|
83
|
+
['beginning_of_previous_quarter', moment.beginning_of_quarter, 'The first second of the previous quarter'],
|
84
|
+
['end_of_previous_quarter', moment.end_of_quarter, 'The last second of the previous quarter'],
|
85
|
+
]
|
86
|
+
moment = time - 1.year
|
87
|
+
moments += [
|
88
|
+
['beginning_of_previous_year', moment.beginning_of_year, 'The first second of the previous year'],
|
89
|
+
['end_of_previous_year', moment.end_of_year, 'The last second of the previous year'],
|
90
|
+
]
|
91
|
+
|
92
|
+
moments
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/hsql/file.rb
CHANGED
@@ -1,12 +1,36 @@
|
|
1
|
-
# HSQL::File parses the input file and provides reader methods to the hash of
|
2
|
-
# YAML data from the front matter section and a list of the queries in the SQL
|
3
|
-
# portion.
|
4
1
|
require 'yaml'
|
5
2
|
require_relative 'query'
|
3
|
+
require_relative 'data'
|
6
4
|
module HSQL
|
7
|
-
|
5
|
+
# HSQL::File parses the input file and provides reader methods to the hash of
|
6
|
+
# YAML data from the front matter section and a list of the queries in the SQL
|
7
|
+
# portion.
|
8
|
+
class File
|
9
|
+
attr_reader :string, :timestamp, :environment
|
10
|
+
|
11
|
+
# This is used to indicate when a source file is malformed.
|
12
|
+
class FormatError < StandardError
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(string, options)
|
16
|
+
@string = string
|
17
|
+
@timestamp = options.fetch(:timestamp, Time.current)
|
18
|
+
@environment = options[:environment]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Given the contents of a SQL file with YAML front matter (see README for an
|
22
|
+
# example) this will return a HSQL::File object providing access to the parts
|
23
|
+
# of that file.
|
24
|
+
def self.parse(string, options)
|
25
|
+
new(string, options).parse!
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.parse_file(file, options)
|
29
|
+
parse(file.read, options)
|
30
|
+
end
|
31
|
+
|
8
32
|
def metadata
|
9
|
-
@metadata ||= ::YAML.load(@front_matter)
|
33
|
+
@metadata ||= @front_matter ? ::YAML.load(@front_matter) : {}
|
10
34
|
end
|
11
35
|
|
12
36
|
def queries
|
@@ -23,9 +47,12 @@ module HSQL
|
|
23
47
|
|
24
48
|
def split!
|
25
49
|
@split ||= begin
|
26
|
-
|
27
|
-
|
28
|
-
|
50
|
+
top_half, divider, rest = string.partition(/^---$/)
|
51
|
+
if divider.present?
|
52
|
+
@front_matter = top_half
|
53
|
+
@sql = rest
|
54
|
+
else # No divider found, therefore no YAML header
|
55
|
+
@sql = top_half
|
29
56
|
end
|
30
57
|
true
|
31
58
|
end
|
@@ -33,20 +60,29 @@ module HSQL
|
|
33
60
|
|
34
61
|
def data
|
35
62
|
@data ||= begin
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end || {}
|
63
|
+
validate_environment_exists!
|
64
|
+
|
65
|
+
hash = metadata['data'] || {}
|
66
|
+
hash = hash[environment] || {} if environment
|
67
|
+
hash.merge(Data.for_machines(timestamp))
|
42
68
|
end
|
43
69
|
end
|
44
70
|
|
71
|
+
def validate_environment_exists!
|
72
|
+
return unless environment
|
73
|
+
return if metadata['data'].blank? || metadata['data'].key?(environment)
|
74
|
+
fail ArgumentError, "The environment #{environment.inspect} is not specified"
|
75
|
+
end
|
76
|
+
|
45
77
|
def interpolate_data!
|
46
78
|
template = Template.new(@sql)
|
47
79
|
template.variable_names.each do |name|
|
48
|
-
|
80
|
+
next if data.key?(name)
|
81
|
+
|
82
|
+
if environment
|
49
83
|
fail FormatError, "#{name.inspect} is not set in #{environment.inspect} environment"
|
84
|
+
else
|
85
|
+
fail FormatError, "#{name.inspect} is not set! Did you provide the right environment argument?"
|
50
86
|
end
|
51
87
|
end
|
52
88
|
|
data/lib/hsql/query.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# PgQuery uses Postgres' own parser to parse and then deparse each query.
|
2
1
|
require 'pg_query'
|
3
2
|
module HSQL
|
3
|
+
# PgQuery uses Postgres' own parser to parse and then deparse each query.
|
4
4
|
class Query < Struct.new(:ast)
|
5
5
|
# Returns a list of queries found in the source SQL
|
6
6
|
def self.parse(source)
|
data/lib/hsql/template.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
require 'mustache'
|
2
2
|
module HSQL
|
3
|
-
|
3
|
+
# Given some SQL that may contain Mustache tags (e.g. {{{ variable }}} ),
|
4
|
+
# accept a hash of data that interpolates each tag.
|
5
|
+
# Throws an error if one of the tag names can't be found in the data.
|
6
|
+
class Template
|
7
|
+
attr_reader :input
|
8
|
+
|
9
|
+
def initialize(input)
|
10
|
+
@input = input
|
11
|
+
end
|
12
|
+
|
4
13
|
def variable_names
|
5
14
|
extract_variable_names(ast).uniq
|
6
15
|
end
|
@@ -17,7 +26,7 @@ module HSQL
|
|
17
26
|
if tree[1] == :fetch
|
18
27
|
tree.last.first
|
19
28
|
else
|
20
|
-
tree.map { |token| extract_variable_names(token) }.compact
|
29
|
+
tree.map { |token| extract_variable_names(token) }.flatten.compact
|
21
30
|
end
|
22
31
|
end
|
23
32
|
|
data/lib/hsql/version.rb
CHANGED
data/lib/hsql.rb
CHANGED
@@ -2,22 +2,3 @@ require_relative 'hsql/version'
|
|
2
2
|
require_relative 'hsql/template'
|
3
3
|
require_relative 'hsql/file'
|
4
4
|
require_relative 'hsql/query'
|
5
|
-
require 'mustache'
|
6
|
-
|
7
|
-
module HSQL
|
8
|
-
# This is used to indicate when a source file is malformed.
|
9
|
-
class FormatError < StandardError
|
10
|
-
end
|
11
|
-
|
12
|
-
# Given the contents of a SQL file with YAML front matter (see README for an
|
13
|
-
# example) this will return a HSQL::File object providing access to the parts
|
14
|
-
# of that file.
|
15
|
-
def self.parse(string, environment)
|
16
|
-
fail ArgumentError, 'The environment argument is required' unless environment
|
17
|
-
File.new(string, environment).parse!
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.parse_file(file, environment)
|
21
|
-
parse(file.read, environment)
|
22
|
-
end
|
23
|
-
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hsql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Danger Canty
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mustache
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,20 @@ dependencies:
|
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '3.3'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: timecop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.8.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.8.0
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: pry-byebug
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,8 +122,7 @@ dependencies:
|
|
94
122
|
- - ">"
|
95
123
|
- !ruby/object:Gem::Version
|
96
124
|
version: '3'
|
97
|
-
description: Write SQL queries
|
98
|
-
how they should be executed
|
125
|
+
description: Write SQL queries with Mustache and easily render them for specific dates/times
|
99
126
|
email:
|
100
127
|
- gems@jackcanty.com
|
101
128
|
executables:
|
@@ -105,6 +132,7 @@ extra_rdoc_files: []
|
|
105
132
|
files:
|
106
133
|
- ".gitignore"
|
107
134
|
- ".rspec"
|
135
|
+
- ".rubocop.yml"
|
108
136
|
- ".ruby-version"
|
109
137
|
- ".travis.yml"
|
110
138
|
- CODE_OF_CONDUCT.md
|
@@ -115,9 +143,11 @@ files:
|
|
115
143
|
- bin/console
|
116
144
|
- bin/hsql
|
117
145
|
- bin/setup
|
146
|
+
- examples/date_math.sql
|
118
147
|
- examples/simple.sql
|
119
148
|
- hsql.gemspec
|
120
149
|
- lib/hsql.rb
|
150
|
+
- lib/hsql/data.rb
|
121
151
|
- lib/hsql/file.rb
|
122
152
|
- lib/hsql/query.rb
|
123
153
|
- lib/hsql/template.rb
|