candy--sql 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f488ce2f5e412a34a8054d2ebb6a77a3ea59fa52
4
+ data.tar.gz: 76e20cad837b9950b3519b6aa2ad03068f946b97
5
+ SHA512:
6
+ metadata.gz: 27f22cf230a8edcccba0c78c3af16963f753adaf3182126e53380faab2e6b87d4e54e2c7345dccec51e3758f28657d55269c55920fdbce25b2d7a2ad1b98f38c
7
+ data.tar.gz: 9553fe961bdbb0b7967a4bcb8cb1fa74e0a8530cc421de1e0765dad52e8d3457444ae70112f4cb82f9b08e14f986b9d7e6be6f10e263c340936b1c86213716dc
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in candy--sql.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 llby
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,102 @@
1
+ # CANDY--SQL
2
+
3
+ ## 概要
4
+ SQLの構文糖です。
5
+
6
+ シンタックスシュガーです。
7
+
8
+ キャンディーです。
9
+
10
+ これはSQLに対しての構文糖や、DSLの類いです。
11
+ 最近は、O/Rマッパーが広がってるようですが
12
+ その時代の流れに逆らい、まだSQLに別の進化があるのではないか?
13
+ と思っています。
14
+
15
+ これを作ろうと思ったのは、私はSQLを書く事が多く
16
+ サブクエリなど長々と書いていると分かりにくくなってきて
17
+ 「もっと簡単に書けないのか?」
18
+ などと思う事が多くありました。
19
+
20
+ SQLが普及してから長く経つので、こういったものがあるのではないか?
21
+ と探してみましたが、なかなか良いものが見つかりませんでした。
22
+
23
+ 世間的にどちらかというと、SQLが長くなりそうな場合は
24
+ プログラム側に処理を移すのが、一般的なやり方だと思います。
25
+ その方が可読性も高まるし、変数なども使用出来ます。
26
+
27
+ それでも私の場合は、SQLを使う場面が多くあり
28
+ しかも長いSQLが多い。
29
+
30
+ それではと思い自分用に作っていましたが
31
+ 「他にも同じように困っている方がいるかもしれない」
32
+ と思い、公開する事にしました。
33
+
34
+ ## 目的
35
+ 時代の流れを、またSQLの方に戻そうという野望があります。
36
+
37
+ ## 前提条件
38
+ SQLが使える方向けです。
39
+
40
+ ## 使い方
41
+ ファイルは、2つのパートに分かれます。
42
+ 上のパートは、定義パートです。
43
+ 下のパートは、SQL構文パートです。
44
+
45
+ ###定義パート
46
+ 定義パートには、SQLでASを使って宣言される
47
+ * テーブル定義
48
+ * SELECT内のカラム定義
49
+ を変数のように定義する事が出来ます。
50
+
51
+ 下記のように記載します。
52
+
53
+ ```
54
+ 変数名== 定義内容
55
+ ```
56
+ また、ASを使わずにそのまま変換される変数も定義する事が出来ます。
57
+
58
+ 下記のように記載します。
59
+
60
+ ```
61
+ 変数名= 定義内容
62
+ ```
63
+ これらは、定義パート、SQL構文パートで展開されますが
64
+ GROUP BY、ORDER BY、HAVINGでは展開されません。
65
+
66
+ ###SQL構文パート
67
+ SQL構文パートは、省略記号を使ってSQLを短く記載する事が出来ます。
68
+
69
+ ```
70
+ S-- /* SELECT */
71
+ F-- /* FROM */
72
+ W-- /* WHERE */
73
+ A- /* AND */
74
+ O- /* OR */
75
+ G-- /* GROUP BY */
76
+ O-- /* ORDER BY */
77
+ H-- /* HAVING */
78
+ ```
79
+ まるで、キャンディのようですね。
80
+ これらの記号は、全て行の先頭でのみ使用可能です。
81
+
82
+ また、「F--」の行では、下記の結合記号が使用出来ます。
83
+
84
+ ```
85
+ =< /* INNER JOIN */
86
+ << /* LEFT JOIN */
87
+ >> /* RIGHT JOIN */
88
+ ```
89
+
90
+ ## 今後について
91
+ SQLからドキュメントの様なものが出力出来ないか?
92
+ と思っています。
93
+
94
+ その他、SQLを書くうえで困る事があれば対応していきたいです。
95
+
96
+
97
+ ## ライセンス
98
+ MITライセンスです。
99
+
100
+ llby@shingor.net
101
+
102
+ Copyright (c) 2015 llby
@@ -0,0 +1,38 @@
1
+ # Candy--Sql
2
+
3
+
4
+ ## Installation
5
+
6
+ Add this line to your application's Gemfile:
7
+
8
+ ```ruby
9
+ gem 'candy--sql'
10
+ ```
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install candy--sql
19
+
20
+
21
+ ## Usage
22
+
23
+ $ bin/make sample/sample1.csql
24
+
25
+ You should make tables in your database before do sample queries.
26
+ Please use sample/seed.sql
27
+
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at
32
+ https://github.com/llby/candy--sql.
33
+
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
38
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "candy--sql"
5
+ require "irb"
6
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'candy--sql'
4
+
5
+ filename = ARGV[0]
6
+ res = CandySql.make( filename )
7
+ puts res
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'candy--sql/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "candy--sql"
8
+ spec.version = CandySql::VERSION
9
+ spec.authors = ["llby"]
10
+ spec.email = ["llby@shingor.net"]
11
+
12
+ spec.summary = %q{Syntax sugar for sql.}
13
+ spec.homepage = "https://github.com/llby/candy--sql"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
@@ -0,0 +1,53 @@
1
+ # coding: utf-8
2
+
3
+ require 'candy--sql/version'
4
+ require 'candy--sql/interpreter'
5
+ require 'candy--sql/token'
6
+ require 'candy--sql/variable'
7
+
8
+ module CandySql
9
+
10
+ def make(filename)
11
+
12
+ mode = 0 #(0: 変数定義、1: SQL構文)
13
+ var = {}
14
+ res = []
15
+
16
+ open(filename) { |file|
17
+ while row = file.gets
18
+
19
+ # トークン分割
20
+ src = CandySql::Token.tokenize(row)
21
+
22
+ if mode == 0
23
+ if src[1] == "=="
24
+ # AS定義
25
+ var = var.merge( CandySql::Valiable.definAs(src, var) )
26
+ elsif src[1] == "="
27
+ # 変数定義
28
+ var = var.merge( CandySql::Valiable.defin(src, var) )
29
+ elsif src[1] == "<"
30
+ # サブクエリ定義
31
+ tmp = "(" + CandySql.make(src[2][1 .. -2]) + ")"
32
+ src.slice!( 2, 2 )
33
+ src.insert(2, tmp)
34
+ src[1] = "="
35
+ var = var.merge( CandySql::Valiable.definAs(src, var) )
36
+ elsif src[1] == "--"
37
+ mode = 1
38
+ end
39
+ end
40
+
41
+ if mode == 1
42
+ # SQL構文
43
+ res << CandySql::Interpreter.statement(src, var)
44
+ end
45
+
46
+ end
47
+ }
48
+
49
+ return res.join("\n")
50
+ end
51
+
52
+ module_function :make
53
+ end
@@ -0,0 +1,79 @@
1
+ # coding: utf-8
2
+
3
+ module CandySql
4
+ module Interpreter
5
+
6
+ def statement(src, var)
7
+
8
+ if src[1] == "--"
9
+ if src[0] == "G"
10
+
11
+ src[0] = "GROUP BY"
12
+
13
+ elsif src[0] == "O"
14
+
15
+ src[0] = "ORDER BY"
16
+
17
+ elsif src[0] == "H"
18
+
19
+ src[0] = "HAVING"
20
+
21
+ else
22
+ if src[0] == "S"
23
+
24
+ src[0] = "SELECT"
25
+
26
+ elsif src[0] == "F"
27
+
28
+ src[0] = "FROM"
29
+
30
+ elsif src[0] == "W"
31
+
32
+ src[0] = "WHERE"
33
+
34
+ end
35
+ src.each_with_index do |t, i|
36
+ if t == "=<"
37
+ src[i] = "INNER JOIN"
38
+ src.insert( i, "\n" )
39
+ elsif t == "<<"
40
+ src[i] = "LEFT JOIN"
41
+ src.insert( i, "\n" )
42
+ elsif t == ">>"
43
+ src[i] = "RIGHT JOIN"
44
+ src.insert( i, "\n" )
45
+ elsif var.key?(t)
46
+ src[i] = var[t]
47
+ if src[i+1] == ","
48
+ src[i+1] = ",\n"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ src.slice!( 1, 1 )
54
+
55
+ elsif src[1] == "-"
56
+
57
+ if src[0] == "A"
58
+
59
+ src[0] = "AND"
60
+
61
+ elsif src[0] == "O"
62
+
63
+ src[0] = "OR"
64
+
65
+ end
66
+ src.slice!( 1, 1 )
67
+ src.each_with_index do |t, i|
68
+ if var.key?(t)
69
+ src[i] = var[t]
70
+ end
71
+ end
72
+ end
73
+
74
+ return src.join(" ").gsub(/\s\(/, "(").gsub(/\s,/, ",")
75
+ end
76
+
77
+ module_function :statement
78
+ end
79
+ end
@@ -0,0 +1,45 @@
1
+ # coding: utf-8
2
+
3
+ module CandySql
4
+ module Token
5
+
6
+ def tokenize(src)
7
+ res = []
8
+
9
+ while !src.empty? do
10
+
11
+ if src =~ /^('.*')/ || src =~ /^(".*")/
12
+
13
+ # 文字列
14
+ res << src.slice!(0,$1.length)
15
+
16
+ elsif src =~ /^([\w\.]+)/
17
+
18
+ # 変数
19
+ res << src.slice!(0,$1.length)
20
+
21
+ elsif src =~ /^([\s\t]+)/
22
+
23
+ # 空白、タブは除外する
24
+ src.slice!(0,$1.length)
25
+
26
+ elsif src =~ /^(--|==|<=|>=|=<|<<|>>)/
27
+
28
+ # 2文字以上記号
29
+ res << src.slice!(0,$1.length)
30
+
31
+ elsif src =~ /^\W/
32
+
33
+ # 記号
34
+ res << src.slice!(0,1)
35
+
36
+ end
37
+
38
+ end
39
+
40
+ return res
41
+ end
42
+
43
+ module_function :tokenize
44
+ end
45
+ end
@@ -0,0 +1,61 @@
1
+ # coding: utf-8
2
+
3
+ module CandySql
4
+ module Valiable
5
+
6
+ def defin(src, var)
7
+ val = []
8
+
9
+ key = src[0]
10
+ # 変数定義
11
+ src.shift(2)
12
+ src.each_with_index{ |s, i|
13
+ if var.key?(s)
14
+ src[i] = var[s]
15
+ end
16
+ }
17
+ var[key] = src.join(" ")
18
+ return var
19
+ end
20
+
21
+ def definAs(src, var)
22
+ val = []
23
+
24
+ key = src[0]
25
+ if src[3] == "["
26
+ # JOIN定義
27
+ val << [src[2], "AS", key]
28
+ src.shift(3)
29
+
30
+ src.each{ |s|
31
+
32
+ if s == "]"
33
+
34
+ else
35
+ if s == "["
36
+ s = "ON"
37
+ elsif var.key?(s)
38
+ s = var[s]
39
+ end
40
+ val << s
41
+ end
42
+ }
43
+ else
44
+ # AS定義
45
+ src.shift(2)
46
+ src.each_with_index{ |s, i|
47
+ if var.key?(s)
48
+ src[i] = var[s]
49
+ end
50
+ }
51
+ src << ["AS", key]
52
+ val = src
53
+ end
54
+ var[key] = val.join(" ")
55
+ return var
56
+ end
57
+
58
+ module_function :defin
59
+ module_function :definAs
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ module CandySql
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,3 @@
1
+ S-- *
2
+ F-- candies
3
+ O-- id
@@ -0,0 +1,4 @@
1
+ S-- count(*)
2
+ F-- sales
3
+ W-- created_at >= '2015-01-01'
4
+ O-- created_at
@@ -0,0 +1,8 @@
1
+ SL== sales
2
+ CD== candies[CD.id = SL.candy_id]
3
+
4
+
5
+ S-- *, SL.num * CD.price
6
+ F-- SL =< CD
7
+ W-- CD.sweet > 2
8
+ O-- created_at
@@ -0,0 +1,9 @@
1
+ SL== sales
2
+ CD== candies[CD.id = SL.candy_id]
3
+ SUB<"sample/sample4_sub.csql">[SUB.Y = DATE_FORMAT(SL.created_at, '%Y')]
4
+
5
+
6
+ S-- DATE_FORMAT(SL.created_at, '%Y-%m'), sum(SL.num * CD.price), sum(SL.num * CD.price) / SUB.SUM_OF_Y * 100
7
+ F-- SL =< CD =< SUB
8
+ G-- DATE_FORMAT(SL.created_at, '%Y-%m')
9
+ O-- SL.created_at
@@ -0,0 +1,10 @@
1
+ Y==DATE_FORMAT(SL.created_at, '%Y')
2
+ SUM_OF_Y== sum(SL.num * CD.price)
3
+ SL== sales
4
+ CD== candies[CD.id = SL.candy_id]
5
+
6
+
7
+ S-- Y, SUM_OF_Y
8
+ F-- SL =< CD
9
+ G-- DATE_FORMAT(SL.created_at, '%Y')
10
+ O-- created_at
@@ -0,0 +1,15 @@
1
+ SUM_NUM_PRI= sum(SL.num * CD.price)
2
+ Y= DATE_FORMAT(SL.created_at, '%Y')
3
+
4
+ YM== DATE_FORMAT(SL.created_at, '%Y-%m')
5
+ SUM_OF_M== SUM_NUM_PRI
6
+ PER_OF_Y== SUM_NUM_PRI / SUB.SUM_OF_Y * 100
7
+ SL== sales
8
+ CD== candies[CD.id = SL.candy_id]
9
+ SUB<"sample/sample4_sub.csql">[SUB.Y = Y]
10
+
11
+
12
+ S-- YM, SUM_OF_M, PER_OF_Y
13
+ F-- SL =< CD =< SUB
14
+ G-- YM
15
+ O-- created_at
@@ -0,0 +1,11 @@
1
+ YM== DATE_FORMAT(SL.created_at, '%Y-%m')
2
+ SUM_OF_M== sum(SL.num * CD.price)
3
+ SL== sales
4
+ CD== candies[CD.id = SL.candy_id]
5
+
6
+
7
+ S-- CD.name, YM, SUM_OF_M
8
+ F-- SL =< CD
9
+ W-- SL.created_at >= '2015-01-01'
10
+ A- CD.sweet > 2
11
+ G-- CD.id, YM
@@ -0,0 +1,49 @@
1
+ CREATE TABLE candies (
2
+ id integer unsigned not null auto_increment,
3
+ name varchar(50) not null default "",
4
+ price integer not null default 0,
5
+ sweet tinyint not null default 0,
6
+ primary key (id)
7
+ )
8
+
9
+ INSERT INTO candies(name, price, sweet)
10
+ VALUES
11
+ ("strawberry", 130, 4),
12
+ ("orange", 120, 3),
13
+ ("lemon", 100, 1),
14
+ ("chocolate", 150, 5);
15
+
16
+
17
+ CREATE TABLE sales (
18
+ id integer unsigned not null auto_increment,
19
+ candy_id integer not null,
20
+ num integer not null default 0,
21
+ created_at datetime not null default current_timestamp,
22
+ primary key (id)
23
+ )
24
+
25
+ INSERT INTO sales(candy_id, num, created_at)
26
+ VALUES
27
+ (1, 6, '2014-01-01'),
28
+ (1, 11, '2014-02-12'),
29
+ (1, 12, '2014-03-08'),
30
+ (1, 9, '2015-06-03'),
31
+ (1, 27, '2015-07-05'),
32
+ (1, 13, '2015-08-09'),
33
+ (2, 26, '2014-04-21'),
34
+ (2, 25, '2014-05-22'),
35
+ (2, 19, '2015-05-04'),
36
+ (2, 14, '2015-07-30'),
37
+ (3, 20, '2014-03-09'),
38
+ (3, 28, '2015-02-11'),
39
+ (3, 21, '2015-04-20'),
40
+ (3, 9, '2015-11-02'),
41
+ (3, 1, '2015-12-03'),
42
+ (3, 18, '2015-06-07'),
43
+ (3, 7, '2015-03-25'),
44
+ (4, 2, '2014-12-07'),
45
+ (4, 4, '2015-02-22'),
46
+ (4, 3, '2015-04-25');
47
+
48
+
49
+
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: candy--sql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - llby
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-10-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ - llby@shingor.net
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - .travis.yml
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.jp.md
53
+ - README.md
54
+ - Rakefile
55
+ - bin/console
56
+ - bin/make
57
+ - bin/setup
58
+ - candy--sql.gemspec
59
+ - lib/candy--sql.rb
60
+ - lib/candy--sql/interpreter.rb
61
+ - lib/candy--sql/token.rb
62
+ - lib/candy--sql/variable.rb
63
+ - lib/candy--sql/version.rb
64
+ - sample/sample1.csql
65
+ - sample/sample2.csql
66
+ - sample/sample3.csql
67
+ - sample/sample4.csql
68
+ - sample/sample4_sub.csql
69
+ - sample/sample5.csql
70
+ - sample/sample6.csql
71
+ - sample/seed.sql
72
+ homepage: https://github.com/llby/candy--sql
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.2.3
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Syntax sugar for sql.
96
+ test_files: []