Going public

This commit is contained in:
Loic Nageleisen 2017-02-27 19:18:03 +01:00
commit 77c44a0b2d
7 changed files with 331 additions and 0 deletions

7
Gemfile Normal file
View file

@ -0,0 +1,7 @@
source 'https://rubygems.org'
gemspec
gem 'rubocop'
gem 'minitest'
gem 'pry'

41
Gemfile.lock Normal file
View file

@ -0,0 +1,41 @@
PATH
remote: .
specs:
rebel (0.1.0)
GEM
remote: https://rubygems.org/
specs:
ast (2.3.0)
coderay (1.1.1)
method_source (0.8.2)
minitest (5.10.1)
parser (2.4.0.0)
ast (~> 2.2)
powerpack (0.1.1)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rainbow (2.2.1)
rubocop (0.47.1)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.8.1)
slop (3.6.0)
unicode-display_width (1.1.3)
PLATFORMS
ruby
DEPENDENCIES
minitest
pry
rebel!
rubocop
BUNDLED WITH
1.14.5

19
LICENSE Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2016 Loic Nageleisen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

37
README.md Normal file
View file

@ -0,0 +1,37 @@
# Rebel - Ruby-flavored SQL
SQL-flavored Ruby.
```
You've been fighting yet another abstraction...
Aren't you fed up with object-relation magic?
But wait, here comes a humongous migration.
Is ActiveRecord making you sick?
To hell with that monstrous ARel expression!
Tell the truth, you were just wishing
That it was as simple as a here-string.
But could it keep some Ruby notation
Instead of silly interpolations?
Stop the menace, time for a revolution!
Rebel!
No magic
No bullshit
No layers
No wrappers
No smarty-pants
No weird stance
No sexy
No AST
No lazy loading
No crazy mapping
No pretense
No nonsense
What you write is what you get: readable and obvious
What you write is what you meant: tasty and delicious
Wait, it doesn't execute!?
Just use your fave client gem, isn't that cute?
```

3
lib/rebel.rb Normal file
View file

@ -0,0 +1,3 @@
module Rebel; end
require 'rebel/sql'

213
lib/rebel/sql.rb Normal file
View file

@ -0,0 +1,213 @@
module Rebel::SQL
attr_reader :conn
def exec(query)
conn.exec(query)
end
def create_table(table_name, desc)
exec(SQL.create_table(table_name, desc))
end
def select(*fields, from: nil, where: nil, inner: nil, left: nil, right: nil)
exec(SQL.select(*fields,
from: from,
where: where,
inner: inner,
left: left,
right: right))
end
def insert_into(table_name, *rows)
exec(SQL.insert_into(table_name, *rows))
end
def update(table_name, set: nil, where: nil, inner: nil, left: nil, right: nil)
exec(SQL.update(table_name, set: set, where: where, inner: inner, left: left, right: right))
end
def delete_from(table_name, where: nil, inner: nil, left: nil, right: nil)
exec(SQL.delete_from(table_name, where: where, inner: inner, left: left, right: right))
end
def truncate(table_name)
exec(SQL.truncate(table_name))
end
def count(*n)
SQL.count(*n)
end
def join(table, on: nil)
SQL.join(table, on: on)
end
def outer_join(table, on: nil)
SQL.outer_join(table, on: on)
end
class Raw < String
def as(n)
Raw.new(self + " AS #{SQL.name(n)}")
end
def as?(n)
n ? as(n) : self
end
def on(clause)
Raw.new(self + " ON #{SQL.and_clause(clause)}")
end
def on?(clause)
clause ? on(clause) : self
end
end
class << self
def raw(str)
Raw.new(str)
end
def create_table(table_name, desc)
<<-SQL
CREATE TABLE #{SQL.name(table_name)} (
#{SQL.list(desc.map { |k, v| "#{SQL.name(k)} #{v}" })}
);
SQL
end
def select(*fields, from: nil, where: nil, inner: nil, left: nil, right: nil)
<<-SQL
SELECT #{names(*fields)} FROM #{name(from)}
#{SQL.inner?(inner)}
#{SQL.left?(left)}
#{SQL.right?(right)}
#{SQL.where?(where)};
SQL
end
def insert_into(table_name, *rows)
<<-SQL
INSERT INTO #{SQL.name(table_name)} (#{SQL.names(*rows.first.keys)})
VALUES #{SQL.list(rows.map { |r| "(#{SQL.values(*r.values)})" })};
SQL
end
def update(table_name, set: nil, where: nil, inner: nil, left: nil, right: nil)
fail ArgumentError if set.nil?
<<-SQL
UPDATE #{SQL.name(table_name)}
SET #{SQL.assign_clause(set)}
#{SQL.inner?(inner)}
#{SQL.left?(left)}
#{SQL.right?(right)}
#{SQL.where?(where)};
SQL
end
def delete_from(table_name, where: nil, inner: nil, left: nil, right: nil)
<<-SQL
DELETE FROM #{SQL.name(table_name)}
#{SQL.inner?(inner)}
#{SQL.left?(left)}
#{SQL.right?(right)}
#{SQL.where?(where)};
SQL
end
def truncate(table_name)
<<-SQL
TRUNCATE #{SQL.name(table_name)};
SQL
end
## Functions
def count(*n)
raw("COUNT(#{names(*n)})")
end
def join(table, on: nil)
raw("JOIN #{name(table)}").on?(on)
end
def outer_join(table, on: nil)
raw("OUTER JOIN #{name(table)}").on?(on)
end
## Support
def name(name)
return name if name.is_a?(Raw)
return raw('*') if name == '*'
name.to_s.split('.').map { |e| "\"#{e}\"" }.join('.')
end
def names(*names)
list(names.map { |k| name(k) })
end
def list(*items)
items.join(', ')
end
def value(v)
case v
when Raw then v
when String then raw "'#{v.tr("'", "''")}'"
when Fixnum then raw v.to_s
when nil then raw 'NULL'
else fail NotImplementedError, v.inspect
end
end
def values(*values)
list(values.map { |v| value(v) })
end
def name_or_value(item)
item.is_a?(Symbol) ? name(item) : value(item)
end
def equal(l, r)
"#{name_or_value(l)} = #{name_or_value(r)}"
end
def assign_clause(clause)
list(clause.map { |k, v| equal(k, v) })
end
def and_clause(clause)
return clause if clause.is_a?(Raw) || clause.is_a?(String)
clause.map do |e|
case e
when Array then "#{name(e[0])} = #{name_or_value(e[1])}"
when Raw, String then e
else fail NotImplementedError, e.class
end
end.join(' AND ')
end
def where?(clause)
return "WHERE #{clause}" if clause.is_a?(Raw) || clause.is_a?(String)
(clause && clause.any?) ? "WHERE #{SQL.and_clause(clause)}" : nil
end
def inner?(join)
join ? "INNER #{join}" : nil
end
def left?(join)
join ? "LEFT #{join}" : nil
end
def right?(join)
join ? "RIGHT #{join}" : nil
end
end
end

11
rebel.gemspec Normal file
View file

@ -0,0 +1,11 @@
Gem::Specification.new do |s|
s.name = 'rebel'
s.version = '0.1.0'
s.licenses = ['MIT']
s.summary = "Fight against the Object tyranny"
s.description = "Write SQL queries in Ruby, or is it the other way around?"
s.authors = ["Loic Nageleisen"]
s.email = 'loic.nageleisen@gmail.com'
s.files = ["lib/**/*.rb"]
s.homepage = 'https://github.com/lloeki/rebel.git'
end