hsql 0.3.8 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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