hsql 0.3.8 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92a6d1e3fd48b97e097cd313b872e3b4846099b5
4
- data.tar.gz: cf1ccc9268ec22de5e1f4c1b30919a91bf1a4aa4
3
+ metadata.gz: 8f0f814ecf9e392ba9fdaf7b9ca4189cf5d938ce
4
+ data.tar.gz: 03b44002a2c25d8ae9aad92662e942af90f30d4c
5
5
  SHA512:
6
- metadata.gz: da5c1bdd69680ddaa11ed50cf55f30fd69ecdb60f55c8ff04cce8b0eeb28451cb4e1faf09d59226ddaa516757e080b1fd5d0407b19c9cd91ea3a7ce0a75b375f
7
- data.tar.gz: 480d4571781e006096bdcc4834c3ae77cf481ba0d99e56a086b5dba3cb8a32131569cf10b957f5417af48e096b781d6c46704aa8201f9bcd007c2d672fb9fd00
6
+ metadata.gz: f403e66309574a45a882bfb52150273a83d15ed2868b382bc89520943128459d7888b36f4fdfcbed03f8ca39be6adb6b1ff19845baf82035f2c7da388e7843b4
7
+ data.tar.gz: d789ce8019449d9b7cf20d61ee84cca6f4584dba60750074d1d9a1b288c6109e706b0e93c4eb2a4e9834a78b798078a0ad54685f95754632d82588c68b5f92f7
data/README.md CHANGED
@@ -11,13 +11,13 @@ the program executing the SQL can use.
11
11
 
12
12
  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
- should be written to a .sql file directly.
14
+ should be written into a .sql file directly.
15
15
 
16
16
  ```sql
17
17
  # filename: daily_summary.sql
18
18
  owner: jackdanger
19
19
  schedule: hourly
20
- data:
20
+ environments:
21
21
  production:
22
22
  output_table: summaries
23
23
  update_condition:
@@ -29,57 +29,168 @@ INSERT INTO {{{output_table}}} SELECT * FROM interesting_information;
29
29
  UPDATE summaries_performed SET complete = 1 {{{update_condition}}};
30
30
  ```
31
31
 
32
- The above is a SQL file and any text editor will allow analysts to use
32
+ The above is a `.sql` file and any text editor will allow analysts to use
33
33
  code completion and syntax highlighting for their queries.
34
34
 
35
35
  The `data` hash in the YAML front matter lists a set of variables, by
36
36
  environment, that can be interpolated into the SQL queries. To render
37
- the queries an environment must be provided.
37
+ the queries an environment must be provided. The templating system used
38
+ for interpolating data is [Mustache](https://mustache.github.io/)
39
+ (though theoretically we could use any templating system).
38
40
 
41
+ ### Rendering SQL on the command line
39
42
  ```bash
40
- $ hsql daily_summary.sql development
43
+ $ hsql daily_summary.sql -env development
41
44
  USE some_database;
42
45
  INSERT INTO jackdanger_summaries SELECT * FROM interesting_information;
43
46
  UPDATE summaries_performed SET complete = 1 WHERE 1 <> 1;
44
47
  ```
45
48
 
46
- The `hsql` command-line utility allows these SQL source files to be
47
- easily run in the context of some other application that understands
48
- when and where to execute the queries.
49
+ The `hsql` command-line utility outputs valid Postgres SQL with any
50
+ template variables filled in from the YAML data.
51
+ To access the metadata directly use the `--yaml` or `--json` flag
49
52
 
50
- To access the metadata directly there is a simple programmatic API:
53
+ ```bash
54
+ $ hsql daily_summary.sql -env development --json
55
+ {"owner":"jackdanger","schedule":"hourly","output_table":"jackdanger_summaries","update_condition":"WHERE 1 <> 1"}
56
+ $ hsql daily_summary.sql -env development --yaml
57
+ ---
58
+ owner: jackdanger
59
+ schedule: hourly
60
+ output_table: jackdanger_summaries
61
+ update_condition: WHERE 1 <> 1
62
+ ```
51
63
 
52
- ```ruby
53
- >> file = HSQL.parse_file('./daily_summary.sql', 'development')
54
- >> file.queries
55
- => [
56
- "USE some_database;",
57
- "INSERT INTO jackdanger_summaries SELECT * FROM interesting_information;",
58
- "UPDATE summaries_performed SET complete = 1 WHERE 1 <> 1;"
59
- ]
64
+ ### Working with times and dates
65
+ By default when you run the `hsql` command it will set the template
66
+ variable `{{{ now }}}` to the current moment (as per the local
67
+ machine's clock). You can modify this by setting a `--timestamp`
68
+ command line flag to any Ruby-parseable time or date string and that
69
+ will be used to establish the value of `{{{ now }}}`.
70
+
71
+ To avoid having to do date math in SQL all of the following are also
72
+ available in every template, relative to the value of `{{{ now }}}`:
73
+
74
+ ```
75
+ "now" => "'2015-10-06 12:34:55 -0700'",
76
+ "beginning_of_hour" => "'2015-10-06 12:00:00 -0700'",
77
+ "beginning_of_day" => "'2015-10-06 00:00:00 -0700'",
78
+ "beginning_of_week" => "'2015-10-05 00:00:00 -0700'",
79
+ "beginning_of_month" => "'2015-10-01 00:00:00 -0700'",
80
+ "beginning_of_quarter" => "'2015-10-01 00:00:00 -0700'",
81
+ "beginning_of_year" => "'2015-01-01 00:00:00 -0800'",
82
+ "end_of_hour" => "'2015-10-06 12:59:59 -0700'",
83
+ "end_of_day" => "'2015-10-06 23:59:59 -0700'",
84
+ "end_of_week" => "'2015-10-11 23:59:59 -0700'",
85
+ "end_of_month" => "'2015-10-31 23:59:59 -0700'",
86
+ "end_of_quarter" => "'2015-12-31 23:59:59 -0800'",
87
+ "end_of_year" => "'2015-12-31 23:59:59 -0800'",
88
+ "beginning_of_previous_hour" => "'2015-10-06 11:00:00 -0700'",
89
+ "end_of_previous_hour" => "'2015-10-06 11:59:59 -0700'",
90
+ "beginning_of_previous_day" => "'2015-10-05 00:00:00 -0700'",
91
+ "end_of_previous_day" => "'2015-10-05 23:59:59 -0700'",
92
+ "beginning_of_previous_week" => "'2015-09-28 00:00:00 -0700'",
93
+ "end_of_previous_week" => "'2015-10-04 23:59:59 -0700'",
94
+ "beginning_of_previous_month" => "'2015-09-01 00:00:00 -0700'",
95
+ "end_of_previous_month" => "'2015-09-30 23:59:59 -0700'",
96
+ "beginning_of_previous_quarter" => "'2015-07-01 00:00:00 -0700'",
97
+ "end_of_previous_quarter" => "'2015-09-30 23:59:59 -0700'",
98
+ "beginning_of_previous_year" => "'2014-01-01 00:00:00 -0800'",
99
+ "end_of_previous_year" => "'2014-12-31 23:59:59 -0800'"
60
100
  ```
61
101
 
62
- The object returned from `HSQL.parse_file` provides you access to both
63
- the rendered queries and the data specified in the front matter. You can
64
- use this to schedule the queries, to run them and send failure notices
65
- to a list of watchers. It's a general-purpose store of data for the
66
- query author to configure whatever system you use to run ETL queries.
102
+ ### Programmatic Ruby API
103
+
104
+ If you'd like to manipulate the queries in some advanced way
105
+ (e.g. to modify the AST of a parsed query) you can use the gem as a
106
+ dependency.
67
107
 
68
108
  ```ruby
69
- >> file = HSQL.parse_file('./daily_summary.sql', 'development')
70
- >> file.yaml
109
+ >> require 'hsql'
110
+ >> file = HSQL::File.parse_file('./simple.sql', 'development')
111
+ >> query = file.queries.first;
112
+ >> query.to_s
113
+ => "INSERT INTO jackdanger_summaries SELECT count(*) FROM interesting_information"
114
+ >> query.ast
71
115
  => {
72
- 'owner' => 'jackdanger',
73
- 'schedule' => 'hourly',
74
- 'data' => {
75
- 'production' => {
76
- 'output_table' => 'summaries',
77
- 'update_condition' => nil,
78
- },
79
- 'development' => {
80
- 'output_table' => 'jackdanger_summaries',
81
- 'update_condition' => 'WHERE 1 <> 1',
82
- },
83
- }
116
+ "INSERT INTO" => {
117
+ "relation" => {
118
+ "RANGEVAR" => {
119
+ "schemaname" => nil,
120
+ "relname" => "jackdanger_summaries",
121
+ "inhOpt" => 2,
122
+ "relpersistence" => "p",
123
+ "alias" => nil,
124
+ "location" => 13
125
+ }
126
+ },
127
+ "cols" => nil,
128
+ "selectStmt" => {
129
+ "SELECT" => {
130
+ "distinctClause" => nil,
131
+ "intoClause" => nil,
132
+ "targetList" => [
133
+ [0] {
134
+ "RESTARGET" => {
135
+ "name" => nil,
136
+ "indirection" => nil,
137
+ "val" => {
138
+ "FUNCCALL" => {
139
+ "funcname" => [
140
+ [0] "count"
141
+ ],
142
+ "args" => nil,
143
+ "agg_order" => nil,
144
+ "agg_filter" => nil,
145
+ "agg_within_group" => false,
146
+ "agg_star" => true,
147
+ "agg_distinct" => false,
148
+ "func_variadic" => false,
149
+ "over" => nil,
150
+ "location" => 79
151
+ }
152
+ },
153
+ "location" => 79
154
+ }
155
+ }
156
+ ],
157
+ "fromClause" => [
158
+ [0] {
159
+ "RANGEVAR" => {
160
+ "schemaname" => nil,
161
+ "relname" => "interesting_information",
162
+ "inhOpt" => 2,
163
+ "relpersistence" => "p",
164
+ "alias" => nil,
165
+ "location" => 95
166
+ }
167
+ }
168
+ ],
169
+ "whereClause" => nil,
170
+ "groupClause" => nil,
171
+ "havingClause" => nil,
172
+ "windowClause" => nil,
173
+ "valuesLists" => nil,
174
+ "sortClause" => nil,
175
+ "limitOffset" => nil,
176
+ "limitCount" => nil,
177
+ "lockingClause" => nil,
178
+ "withClause" => nil,
179
+ "op" => 0,
180
+ "all" => false,
181
+ "larg" => nil,
182
+ "rarg" => nil
183
+ }
184
+ },
185
+ "returningList" => nil,
186
+ "withClause" => nil
187
+ }
84
188
  }
189
+ >> query.ast['INSERT INTO']['relation']['RANGEVAR']['relname'] = 'othertable'
190
+ => "othertable"
191
+ >> query.to_s
192
+ => "INSERT INTO othertable SELECT count(*) FROM interesting_information"
85
193
  ```
194
+
195
+ Please don't hesitate to open a PR or issue for any reason. New
196
+ contributors and bug fixes are welcome. Forks of this project will be celebrated.
data/bin/hsql CHANGED
@@ -19,7 +19,7 @@ end
19
19
 
20
20
  require_relative '../lib/hsql'
21
21
 
22
- file = HSQL::File.parse_file(file, options)
22
+ file = HSQL::File.parse_file(filename, options)
23
23
  if 'yaml' == options[:meta_only]
24
24
  puts file.to_yaml
25
25
  elsif 'json' == options[:meta_only]
data/examples/simple.sql CHANGED
@@ -5,7 +5,7 @@ requires:
5
5
  - daily_payments
6
6
  - hourly_users
7
7
  - some_other_value
8
- data:
8
+ environments:
9
9
  production:
10
10
  output_table: summaries
11
11
  update_condition:
data/lib/hsql/file.rb CHANGED
@@ -17,15 +17,15 @@ module HSQL
17
17
  @verbose = options[:verbose]
18
18
  end
19
19
 
20
- # Given the contents of a SQL file with YAML front matter (see README for an
21
- # example) this will return a HSQL::File object providing access to the parts
22
- # of that file.
23
- def self.parse(string, options)
24
- new(string, options).parse!
20
+ # Given a SQL file with YAML front matter (see README for an
21
+ # example) this will return a HSQL::File object providing access to
22
+ # the parts of that file.
23
+ def self.parse_file(filename, options)
24
+ parse(::File.read(filename), options)
25
25
  end
26
26
 
27
- def self.parse_file(file, options)
28
- parse(file.read, options)
27
+ def self.parse(source, options)
28
+ new(source, options).parse!
29
29
  end
30
30
 
31
31
  def to_yaml
@@ -37,7 +37,11 @@ module HSQL
37
37
  end
38
38
 
39
39
  def metadata
40
- @metadata ||= @front_matter ? ::YAML.load(@front_matter) : {}
40
+ @metadata ||= begin
41
+ hash = @front_matter ? ::YAML.load(@front_matter) : {}
42
+ environments = hash.delete('environments') || {}
43
+ hash.merge(environments[environment] || {})
44
+ end
41
45
  end
42
46
 
43
47
  def queries
@@ -46,12 +50,18 @@ module HSQL
46
50
 
47
51
  def parse!
48
52
  split!
49
- interpolate_data!
53
+ interpolate_metadata!
50
54
  self
51
55
  end
52
56
 
53
57
  private
54
58
 
59
+ # Insert the `environments:` data for the given environment into our
60
+ # SQL queries.
61
+ def template_data
62
+ @data ||= metadata.merge(Data.for_machines(timestamp))
63
+ end
64
+
55
65
  def split!
56
66
  @split ||= begin
57
67
  top_half, divider, rest = string.partition(/^---$/)
@@ -65,17 +75,8 @@ module HSQL
65
75
  end
66
76
  end
67
77
 
68
- def data
69
- @data ||= begin
70
- hash = metadata['data'] || {}
71
- hash = hash.merge(hash[environment] || {})
72
- hash.merge(Data.for_machines(timestamp))
73
- end
74
- end
75
-
76
- def interpolate_data!
77
- # Insert the `data:` section of YAML for the given environment into our SQL queries.
78
- @rendered_sql = Template.new(@sql, @verbose).render(data)
78
+ def interpolate_metadata!
79
+ @rendered_sql = Template.new(@sql, @verbose).render(template_data)
79
80
  end
80
81
  end
81
82
  end
data/lib/hsql/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # http://semver.org/
2
2
  module HSQL
3
- VERSION = '0.3.8'
3
+ VERSION = '0.4.0'
4
4
  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.3.8
4
+ version: 0.4.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-10-02 00:00:00.000000000 Z
11
+ date: 2015-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mustache