hsql 0.2.1 → 0.3.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 +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
|