hsql 0.1.0 → 0.2.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: b36d377513ad3f4e47233608a6c6a3d10abc5400
4
- data.tar.gz: cfa80fcbd40a0f23e715d84274d6f60be9ae56b9
3
+ metadata.gz: 28ac7b37a61c29be9bf0bcda436fd0c493e9700b
4
+ data.tar.gz: 64551381b85a0906d715ad66bb877fd2abc9f435
5
5
  SHA512:
6
- metadata.gz: 0ceda7a4890b22ce3a751b249d4d6a13b2db52093086ab6b5b7cdefd045e77c99779676a39954ba14b14ce2e64556110e4a39a89b71bcbfd097ad5f5438c4e1c
7
- data.tar.gz: 46ff5428b17d28372906f0d92e7f04846337165e36a5a447c6b2036a2ef87133f62024e9e80aa38758d4d498fe5e3dc5cd7accfccb78f6cfa4001b13546e8a3e
6
+ metadata.gz: 73bfea62d54d190aa8057789da2099d38929b910b491140e9011d7f059b2495d0f0370d5e2bc78c058f3c1da0a9a65db94b4c180ced8714dda77489111e99e75
7
+ data.tar.gz: 1c19e860682b308ebc8e979117aecdc254069e4a07a142f3157c2ed7996b2f2c689bcb1c4602a9522500fdce162a6a55746150d986688e8f56e223091833d530
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in hsql.gemspec
4
4
  gemspec
5
+
6
+ gem 'pg_query', git: 'https://github.com/lfittl/pg_query.git'
data/README.md CHANGED
@@ -1,31 +1,83 @@
1
1
  # HSQL
2
2
 
3
- ".SQL ETL" is a library that parses `.sql` files with YAML front matter.
4
- This allows analysts and other non-developers to write and develop ETLs
5
- without having to write source code but still giving them the power of
3
+ "Hash (of data) and SQL" is a library that parses `.sql` files with YAML
4
+ [front matter](http://jekyllrb.com/docs/frontmatter/). This allows
5
+ analysts and other non-developers to write and develop ETLs without
6
+ having to write source code but still giving them the power of
6
7
  specifying variables to interpolate into the SQL and other metadata that
7
8
  the program executing the SQL can use.
8
9
 
9
10
  ## How to use this
10
11
 
11
12
  Rather than specifying variables and metadata for a set of database
12
- queries in a .rb or other programming language source file the queries
13
+ queries in a `.rb`,`.py` or other programming language source file the queries
13
14
  should be written to a .sql file directly.
14
15
 
15
- # filename: daily_summary.sql
16
- owner: jackdanger
17
- schedule: hourly
18
- data:
19
- production:
20
- output_table: summaries
21
- update_condition:
22
- development:
23
- output_table: jackdanger_summaries
24
- update_condition: WHERE 1 <> 1
25
- ---
26
- USE some_database;
27
- INSERT INTO {{{output_table}}} SELECT * FROM interesting_information;
28
- UPDATE summaries_performed SET complete = 1 {{{update_condition}}};
16
+ # filename: daily_summary.sql
17
+ owner: jackdanger
18
+ schedule: hourly
19
+ data:
20
+ production:
21
+ output_table: summaries
22
+ update_condition:
23
+ development:
24
+ output_table: jackdanger_summaries
25
+ update_condition: WHERE 1 <> 1
26
+ ---
27
+ INSERT INTO {{{output_table}}} SELECT * FROM interesting_information;
28
+ UPDATE summaries_performed SET complete = 1 {{{update_condition}}};
29
29
 
30
30
  The above is a SQL file and any text editor will allow analysts to use
31
31
  code completion and syntax highlighting for their queries.
32
+
33
+ The `data` hash in the YAML front matter lists a set of variables, by
34
+ environment, that can be interpolated into the SQL queries. To render
35
+ the queries an environment must be provided.
36
+
37
+ ```bash
38
+ $ hsql daily_summary.sql development
39
+ USE some_database;
40
+ INSERT INTO jackdanger_summaries SELECT * FROM interesting_information;
41
+ UPDATE summaries_performed SET complete = 1 WHERE 1 <> 1;
42
+ ```
43
+
44
+ The `hsql` command-line utility allows these SQL source files to be
45
+ easily run in the context of some other application that understands
46
+ when and where to execute the queries.
47
+
48
+ To access the metadata directly there is a simple programmatic API:
49
+
50
+ ```ruby
51
+ >> file = HSQL.parse_file('./daily_summary.sql', 'development')
52
+ >> file.queries
53
+ => [
54
+ "USE some_database;",
55
+ "INSERT INTO jackdanger_summaries SELECT * FROM interesting_information;",
56
+ "UPDATE summaries_performed SET complete = 1 WHERE 1 <> 1;"
57
+ ]
58
+ ```
59
+
60
+ The object returned from `HSQL.parse_file` provides you access to both
61
+ the rendered queries and the data specified in the front matter. You can
62
+ use this to schedule the queries, to run them and send failure notices
63
+ to a list of watchers. It's a general-purpose store of data for the
64
+ query author to configure whatever system you use to run ETL queries.
65
+
66
+ ```ruby
67
+ >> file = HSQL.parse_file('./daily_summary.sql', 'development')
68
+ >> file.yaml
69
+ => {
70
+ 'owner' => 'jackdanger',
71
+ 'schedule' => 'hourly',
72
+ 'data' => {
73
+ 'production' => {
74
+ 'output_table' => 'summaries',
75
+ 'update_condition' => nil,
76
+ },
77
+ 'development' => {
78
+ 'output_table' => 'jackdanger_summaries',
79
+ 'update_condition' => 'WHERE 1 <> 1',
80
+ },
81
+ }
82
+ }
83
+ ```
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
data/bin/console CHANGED
@@ -1,14 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "hsql"
3
+ require 'bundler/setup'
4
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
5
+ require 'hsql'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
8
9
 
9
10
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start
11
+ require 'pry'
12
+ Pry.start
data/bin/hsql ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'yaml'
5
+ require 'json'
6
+
7
+ options = {}
8
+ option_parser = OptionParser.new do |opts|
9
+ opts.banner = <<-BANNER
10
+ USAGE: #{__FILE__} file environment [--yaml]
11
+
12
+ file: Any *.sql file that has a YAML header ending in three hyphens.
13
+ environment: Must be one of the keys of the "data" hash in the YAML header of the .sql file.
14
+ --yaml: Outputs only the YAML metadata from the file, not the SQL queries themselves.
15
+
16
+ for details, run:
17
+ open https://github.com/JackDanger/hsql
18
+ BANNER
19
+
20
+ opts.on('-y', '--yaml', 'Output metadata for this file as YAML') do |option|
21
+ options[:meta_only] = 'yaml'
22
+ end
23
+ opts.on('-j', '--json', 'Output metadata for this file as JSON') do |option|
24
+ options[:meta_only] = 'json'
25
+ end
26
+ end
27
+
28
+ option_parser.parse!
29
+
30
+ filename, env = ARGV
31
+
32
+ unless filename && env
33
+ puts option_parser.banner
34
+ exit 1
35
+ end
36
+
37
+ begin
38
+ file = File.new(filename)
39
+ rescue Errno::ENOENT
40
+ puts "#{filename.inspect}: No such file"
41
+ exit 1
42
+ end
43
+
44
+ require_relative '../lib/hsql'
45
+
46
+ file = HSQL.parse_file(file, env)
47
+ if 'yaml' == options[:meta_only]
48
+ puts file.metadata.to_yaml
49
+ elsif 'json' == options[:meta_only]
50
+ puts file.metadata.to_json
51
+ else
52
+ file.queries.each do |query|
53
+ # Runs the query through the parser and then deparses it
54
+ puts query
55
+ end
56
+ end
@@ -0,0 +1,18 @@
1
+ # filename: daily_summary.sql
2
+ owner: jackdanger
3
+ schedule: hourly
4
+ requires:
5
+ - daily_payments
6
+ - hourly_users
7
+ - some_other_value
8
+ data:
9
+ production:
10
+ output_table: summaries
11
+ update_condition:
12
+ development:
13
+ output_table: jackdanger_summaries
14
+ update_condition: WHERE 1 <> 1
15
+ ---
16
+ INSERT INTO {{{output_table}}} -- this query is joined to one line
17
+ SELECT COUNT(*) FROM interesting_information; -- and the comments get stripped
18
+ UPDATE summaries_performed SET complete = 1 {{{update_condition}}};
data/hsql.gemspec CHANGED
@@ -4,24 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'hsql/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "hsql"
8
- spec.version = Setl::VERSION
9
- spec.authors = ["Jack Danger Canty"]
10
- spec.email = ["gems@jackcanty.com"]
7
+ spec.name = 'hsql'
8
+ spec.version = HSQL::VERSION
9
+ spec.authors = ['Jack Danger Canty']
10
+ spec.email = ['gems@jackcanty.com']
11
11
 
12
- spec.summary = %q{Store a hash of data with your SQL queries.}
13
- spec.description = %q{Write SQL queries in a .sql format and ship them with metadata about how they should be executed}
14
- spec.homepage = "https://github.com/JackDanger/hsql"
15
- spec.license = "MIT"
12
+ spec.summary = 'Store a hash of data with your SQL queries.'
13
+ spec.description = 'Write SQL queries in a .sql format and ship them with metadata about how they should be executed'
14
+ spec.homepage = 'https://github.com/JackDanger/hsql'
15
+ spec.license = 'MIT'
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
18
+ spec.bindir = 'exe'
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
21
21
 
22
- spec.add_dependency "mustache", '~> 0'
23
- spec.add_development_dependency "bundler", "~> 1.10"
24
- spec.add_development_dependency "rake", '~> 0'
25
- spec.add_development_dependency "rspec", '~> 0'
26
- spec.add_development_dependency "pry-byebug", '~> 0'
22
+ spec.add_dependency 'mustache', '> 0'
23
+ # spec.add_dependency 'pg_query', '>= 0.6.2'
24
+ spec.add_development_dependency 'bundler', '~> 1.10'
25
+ spec.add_development_dependency 'rubocop', '~> 0.3'
26
+ spec.add_development_dependency 'rake', '> 0'
27
+ spec.add_development_dependency 'rspec', '~> 3.3'
28
+ spec.add_development_dependency 'pry-byebug', '> 3'
27
29
  end
data/lib/hsql.rb CHANGED
@@ -1,6 +1,7 @@
1
- require_relative "hsql/version"
2
- require_relative "hsql/template"
3
- require_relative "hsql/file"
1
+ require_relative 'hsql/version'
2
+ require_relative 'hsql/template'
3
+ require_relative 'hsql/file'
4
+ require_relative 'hsql/query'
4
5
  require 'mustache'
5
6
 
6
7
  module HSQL
@@ -12,7 +13,11 @@ module HSQL
12
13
  # example) this will return a HSQL::File object providing access to the parts
13
14
  # of that file.
14
15
  def self.parse(string, environment)
15
- raise ArgumentError, "The environment argument is required" unless environment
16
+ fail ArgumentError, 'The environment argument is required' unless environment
16
17
  File.new(string, environment).parse!
17
18
  end
19
+
20
+ def self.parse_file(file, environment)
21
+ parse(file.read, environment)
22
+ end
18
23
  end
data/lib/hsql/file.rb CHANGED
@@ -1,15 +1,16 @@
1
1
  # HSQL::File parses the input file and provides reader methods to the hash of
2
2
  # YAML data from the front matter section and a list of the queries in the SQL
3
3
  # portion.
4
+ require 'yaml'
5
+ require_relative 'query'
4
6
  module HSQL
5
7
  class File < Struct.new(:string, :environment)
6
- def yaml
7
- @yaml ||= YAML.load(@front_matter)
8
+ def metadata
9
+ @metadata ||= ::YAML.load(@front_matter)
8
10
  end
9
11
 
10
12
  def queries
11
- # TODO: allow multi-line queries!
12
- @queries ||= @rendered_sql.lines.map(&:chomp).reject(&:empty?)
13
+ @queries ||= Query.parse(@rendered_sql)
13
14
  end
14
15
 
15
16
  def parse!
@@ -24,7 +25,7 @@ module HSQL
24
25
  @split ||= begin
25
26
  @front_matter, divider, @sql = string.partition(/^---$/)
26
27
  unless divider == '---'
27
- raise FormatError, "The YAML front matter is required, otherwise this is just a SQL file"
28
+ fail FormatError, 'The YAML front matter is required, otherwise this is just a SQL file'
28
29
  end
29
30
  true
30
31
  end
@@ -32,11 +33,11 @@ module HSQL
32
33
 
33
34
  def data
34
35
  @data ||= begin
35
- if yaml['data']
36
- unless yaml['data'].key?(environment)
37
- raise ArgumentError, "The environment #{environment.inspect} is not specified"
36
+ if metadata['data']
37
+ unless metadata['data'].key?(environment)
38
+ fail ArgumentError, "The environment #{environment.inspect} is not specified"
38
39
  end
39
- yaml['data'][environment]
40
+ metadata['data'][environment]
40
41
  end || {}
41
42
  end
42
43
  end
@@ -45,7 +46,7 @@ module HSQL
45
46
  template = Template.new(@sql)
46
47
  template.variable_names.each do |name|
47
48
  unless data.key?(name)
48
- raise FormatError, "#{name.inspect} is not set in #{environment.inspect} environment"
49
+ fail FormatError, "#{name.inspect} is not set in #{environment.inspect} environment"
49
50
  end
50
51
  end
51
52
 
data/lib/hsql/query.rb ADDED
@@ -0,0 +1,20 @@
1
+ # PgQuery uses Postgres' own parser to parse and then deparse each query.
2
+ require 'pg_query'
3
+ module HSQL
4
+ class Query < Struct.new(:ast)
5
+ # Returns a list of queries found in the source SQL
6
+ def self.parse(source)
7
+ # Splits on semicolons at the end of the line, eliding any comment that
8
+ # might be there.
9
+ PgQuery.parse(source).parsetree.map do |ast|
10
+ Query.new(ast)
11
+ end
12
+ end
13
+
14
+ # Show the parsed query as reconstructed SQL
15
+ def to_s
16
+ PgQuery.deparse ast
17
+ end
18
+ alias_method :to_sql, :to_s
19
+ end
20
+ end
data/lib/hsql/version.rb CHANGED
@@ -1,3 +1,3 @@
1
- module Setl
2
- VERSION = "0.1.0"
1
+ module HSQL
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hsql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jack Danger Canty
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-08-03 00:00:00.000000000 Z
11
+ date: 2015-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mustache
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
@@ -39,17 +39,31 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.10'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: rubocop
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">"
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">"
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
@@ -58,28 +72,28 @@ dependencies:
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '0'
75
+ version: '3.3'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '0'
82
+ version: '3.3'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: pry-byebug
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - "~>"
87
+ - - ">"
74
88
  - !ruby/object:Gem::Version
75
- version: '0'
89
+ version: '3'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - "~>"
94
+ - - ">"
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: '3'
83
97
  description: Write SQL queries in a .sql format and ship them with metadata about
84
98
  how they should be executed
85
99
  email:
@@ -98,10 +112,13 @@ files:
98
112
  - README.md
99
113
  - Rakefile
100
114
  - bin/console
115
+ - bin/hsql
101
116
  - bin/setup
117
+ - examples/simple.sql
102
118
  - hsql.gemspec
103
119
  - lib/hsql.rb
104
120
  - lib/hsql/file.rb
121
+ - lib/hsql/query.rb
105
122
  - lib/hsql/template.rb
106
123
  - lib/hsql/version.rb
107
124
  homepage: https://github.com/JackDanger/hsql