json_q_l 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2148237db30bf43925f46e545bf042957160a0cb
4
+ data.tar.gz: a07301fb48d08b24ac65dc60c0b0731911ffcb82
5
+ SHA512:
6
+ metadata.gz: d106906db104b915f4c5ebf54b04eeb800a18313353b83c3545af010d685132e58c7d5283afcef35bcd5119b4a7bc026163a8cd05eec5a1693b0c120bf361b2f
7
+ data.tar.gz: bccecddd43c53ba741f2e7ef3e462f9c899dcad5123fcab3c4b0d0a19852e9d13e151f95c808145e23176b14fecbd569b1b0c7e6b3ea3ab6297c77b5b9a19f44
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in json_q_l.gemspec
4
+ gemspec
5
+ require 'pry'
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # JsonQL
2
+
3
+ jql is a small query interface for JSON that supports SELECTs with multiple conditions.
4
+
5
+ ## Use
6
+
7
+ To use jql add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'json_q_l'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install json_q_l
20
+
21
+ After bundling from the command line call jql and pass a JSON file formatted like this:
22
+ ```json
23
+ {
24
+ "events": [
25
+ {
26
+ "occasion": "Birthday party",
27
+ "invited_count": 120,
28
+ "year": 2015,
29
+ "month": 3,
30
+ "day": 14
31
+ },
32
+ {
33
+ "occasion": "Press release",
34
+ "invited_count": 64,
35
+ "year": 2015,
36
+ "month": 6,
37
+ "day": 7,
38
+ "cancelled": true
39
+ }
40
+ ]
41
+ }
42
+ ```
43
+ Where "events" represents a table name and each element of the array a single column.
44
+
45
+
46
+ ```
47
+ jql events.json
48
+ ```
49
+
50
+ Then try executing a command
51
+ ```
52
+ jql~> SELECT * FROM events WHERE year = 2015 AND occasion = 'Birthday party';
53
+ ```
54
+
55
+ Have fun!
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "json_q_l"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (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
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/exe/jql ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json_q_l'
4
+ require 'json'
5
+
6
+ file_path = File.dirname(__FILE__) + "/../" + ARGV.first
7
+ puts file_path
8
+ executor = JsonQL::Executor.new(dataset: JSON.parse(File.read(file_path)))
9
+
10
+ while line = Readline.readline('jql~> ', true)
11
+ begin
12
+ puts executor.execute(line)
13
+ rescue
14
+ puts "Hmm something went wrong"
15
+ end
16
+ end
data/json_q_l.gemspec ADDED
@@ -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 'json_q_l/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "json_q_l"
8
+ spec.version = JsonQL::VERSION
9
+ spec.authors = ["Matt Eddy"]
10
+ spec.email = ["matteddy1@gmail.com"]
11
+
12
+ spec.summary = "A SQL like JSON query language"
13
+ spec.description = "A SQL like JSON query language"
14
+ spec.homepage = "http://gfycat.com/BeautifulWholeAtlanticblackgoby"
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 = ["jql"]
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.9"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
@@ -0,0 +1,31 @@
1
+ module JsonQL
2
+ class Condition < Proc
3
+ def self.create_from_tokens(tokens)
4
+ instance = new { |table|
5
+ set_variables = (available_keys(table) & tokens).map do |required_datum|
6
+ if table[required_datum]
7
+ required_datum + convert_type_if_needed(table[required_datum])
8
+ end
9
+ end
10
+
11
+ eval((set_variables + tokens).join(" "))
12
+ }
13
+ end
14
+
15
+ class << self
16
+ private
17
+
18
+ def convert_type_if_needed(value)
19
+ if value.is_a?(Numeric)
20
+ " = #{value};"
21
+ else
22
+ " = '#{value}';"
23
+ end
24
+ end
25
+
26
+ def available_keys(table)
27
+ table.map { |key, value| key }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ module JsonQL
2
+ class Executor
3
+ def initialize(dataset:)
4
+ @dataset = dataset
5
+ end
6
+
7
+ def execute(query)
8
+ @parser = Parser.new(query)
9
+
10
+ selected_columns
11
+ end
12
+
13
+ def selected_columns
14
+ if filtered_table.empty?
15
+ "Nothing matched query"
16
+ else
17
+ parser.column_select.call(filtered_table)
18
+ end
19
+ end
20
+
21
+ def filtered_table
22
+ if parser.where_index
23
+ tables.map do |table|
24
+ table if parser.filter_conditions_from_tokens.call(table)
25
+ end.compact
26
+ else
27
+ tables
28
+ end
29
+ end
30
+
31
+ def tables
32
+ parser.table_select.call(dataset)
33
+ end
34
+
35
+ attr_reader :dataset, :parser
36
+ end
37
+ end
@@ -0,0 +1,65 @@
1
+ module JsonQL
2
+ class Parser
3
+ def self.run(query)
4
+ new(query)
5
+ end
6
+
7
+ def initialize(query)
8
+ @tokens = Tolkienizer.run(query)
9
+ end
10
+
11
+ def column_select
12
+ Proc.new { |table|
13
+ selected_columns.call(table).map do |column|
14
+ {
15
+ column => table.map { |row| row[column] }
16
+ }
17
+ end.flatten
18
+ }
19
+ end
20
+
21
+ def table_select
22
+ Proc.new { |json_object|
23
+ json_object[tokens.slice(from_index + 1)]
24
+ }
25
+ end
26
+
27
+ def where_index
28
+ tokens.index('WHERE')
29
+ end
30
+
31
+ def filter_conditions_from_tokens
32
+ Condition.create_from_tokens(condition_tokens)
33
+ end
34
+
35
+ private
36
+
37
+ def selected_columns
38
+ Proc.new { |table|
39
+ if column_tokens == ["*"]
40
+ table.first.map { |key, value| key }
41
+ else
42
+ column_tokens
43
+ end
44
+ }
45
+ end
46
+
47
+ def condition_tokens
48
+ tokens.slice(where_index + 1, tokens.length)
49
+ end
50
+
51
+ def column_tokens
52
+ tokens.slice(select_index + 1, from_index - 1)
53
+ end
54
+
55
+ def select_index
56
+ tokens.index('SELECT')
57
+ end
58
+
59
+ def from_index
60
+ tokens.index('FROM')
61
+ end
62
+
63
+ attr_reader :tokens
64
+ end
65
+ end
@@ -0,0 +1,21 @@
1
+ module JsonQL
2
+ class Tolkienizer
3
+ # the lord of the things
4
+
5
+ TOKEN_LOOKUP = {
6
+ "=" => "==",
7
+ "OR" => "||",
8
+ "AND" => "&&"
9
+ }
10
+
11
+ def self.run(query)
12
+ query.split(/\s(?=(?:[^']|'[^']*')*$)/).map do |token|
13
+ if TOKEN_LOOKUP[token.upcase]
14
+ TOKEN_LOOKUP[token.upcase]
15
+ else
16
+ token
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module JsonQL
2
+ VERSION = "1.0.0"
3
+ end
data/lib/json_q_l.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "json_q_l/version"
2
+ require "json_q_l/parser"
3
+ require "json_q_l/tolkienizer"
4
+ require "json_q_l/condition"
5
+ require "json_q_l/executor"
6
+ require "readline"
7
+ module JsonQL
8
+ # Your code goes here...
9
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json_q_l
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Eddy
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-09 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.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
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: A SQL like JSON query language
42
+ email:
43
+ - matteddy1@gmail.com
44
+ executables:
45
+ - jql
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - ".rspec"
51
+ - ".travis.yml"
52
+ - Gemfile
53
+ - README.md
54
+ - Rakefile
55
+ - bin/console
56
+ - bin/setup
57
+ - exe/jql
58
+ - json_q_l.gemspec
59
+ - lib/json_q_l.rb
60
+ - lib/json_q_l/condition.rb
61
+ - lib/json_q_l/executor.rb
62
+ - lib/json_q_l/parser.rb
63
+ - lib/json_q_l/tolkienizer.rb
64
+ - lib/json_q_l/version.rb
65
+ homepage: http://gfycat.com/BeautifulWholeAtlanticblackgoby
66
+ licenses: []
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.4.6
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: A SQL like JSON query language
88
+ test_files: []