Skip to content

Instantly share code, notes, and snippets.

@mhasbini
Last active March 17, 2020 00:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mhasbini/ace22d7a10b891b4d59ea403a9ec2d5f to your computer and use it in GitHub Desktop.
Save mhasbini/ace22d7a10b891b4d59ea403a9ec2d5f to your computer and use it in GitHub Desktop.
require './ruby_on_lane.rb'
# Schema
Schema.define('hello_app_database') do
table :users do
column :string, :name
column :int, :age
end
end
# Models
class User < Model
def self.adult?
self.age > 18
end
end
# Seed
User.create(name: "John Doe", age: 42)
User.create(name: "Tipsy Lala", age: 25)
user = User.create(name: "Lorem Ipsum", age: 19)
user.name == "Lorem Ipsum"
# Routes
get '/' do
'Hello :)'
end
get '/filter' do
filter_age = params['age'] || 20
count = User.where('age', '>', filter_age).count
"Number of users who are older than #{filter_age}: #{count}"
end
require 'sqlite3'
require 'linguistics'
require 'rack'
class Adapter
@@db = nil
def table(_name, &block)
@columns = []
self.instance_eval(&block)
if table_exists?(_name)
@columns.each do |column|
unless column_exists?(_name, column[:name])
add_column(table: _name, column: column[:name], type: column[:type])
end
end
else
create_table(table: _name, columns: @columns)
end
end
def column(_type, _name)
@columns << {
:type => map_type[_type],
:name => _name.to_s
}
end
end
class SQLiteAdapter < Adapter
def initialize(db_name)
@@db = SQLite3::Database.new db_name
end
def self.insert(table, params)
@@db.execute("INSERT INTO #{table} (#{params.keys.join(', ')})
VALUES (#{(['?'] * params.count).join(', ')})", params.values)
end
def self.search(table, field, op, value)
columns = self.get_columns(table)
results = []
@@db
.execute("SELECT #{columns.join(', ')} FROM #{table} WHERE #{field} #{op} #{value}")
.map do |result|
record = {}
result.each_with_index do |val, idx|
record[columns[idx]] = val
end
results << record
end
return results
end
def table_exists?(_name)
table_exists = @@db.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", _name.to_s)
return !table_exists.empty?
end
def self.get_columns(_table)
@@db
.execute(
"SELECT name FROM pragma_table_info(?)",
_table.to_s
)
.flatten
end
def column_exists?(_table, _name)
@@db
.execute(
"SELECT COUNT(*) FROM pragma_table_info(?) WHERE name=?",
_table.to_s,
_name.to_s
)
.flatten
.all? { |n| n == 1}
end
def add_column(table:, column:, type:)
@@db.execute("ALTER TABLE #{table} ADD COLUMN #{column} #{type}")
end
def create_table(table:, columns:)
@@db.execute("CREATE TABLE #{table} (#{inline(columns)})")
end
# convert [{
# :type => "string"
# :name => "test"
# }, ...]
# into: name type, name type, name type,
def inline(columns)
columns
.map { |column| "#{column[:name]} #{column[:type]}" }
.join(", ")
end
def map_type
{
:string => "TEXT",
:int => "INTEGER"
}
end
end
module Schema
def self.define(db_name, &block)
adapter = SQLiteAdapter.new(db_name)
adapter.instance_eval(&block)
end
end
# source https://stackoverflow.com/a/1509939
class String
def to_underscore
self.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
end
Linguistics.use :en
class Model
def self.table
@table ||= self.name.to_underscore.en.plural
end
def self.create(params)
SQLiteAdapter.insert(self.table, params)
self.create_attributes(params)
self
end
def self.create_attributes(params)
SQLiteAdapter.get_columns(self.table).each do |column|
self.class.send(:define_method, column.to_sym) do
params.fetch(column.to_sym, nil)
end
end
end
def self.where(field, op, value)
SQLiteAdapter.search(self.table, field, op, value)
end
end
class User < Model
def self.adult?
self.age > 18
end
end
class Handler
def initialize(&block)
@block = block
end
def params
@request.params
end
def call(env)
@request = Rack::Request.new(env)
@body = self.instance_eval(&@block)
[200, {"Content-Type" => "text/plain"}, [@body]]
end
end
class RubyOnLane
def self.app
@app ||= begin
Rack::Builder.new do
map "/" do
run ->(env) {[200, {'Content-Type' => 'text/plain'}, ['Ruby on Lane']] }
end
end
end
end
end
def get(pattern, &block)
RubyOnLane.app.map(pattern) do
run Handler.new(&block)
end
end
require 'rack/server'
if ARGV.length > 1
puts "Usage: ruby runner.rb app_name"
end
if ARGV.length == 1
app_name = ARGV[0]
unless app_name.end_with?('.rb')
app_name = app_name + '.rb'
end
end
require File.expand_path(app_name)
Rack::Server.start :app => RubyOnLane.app
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment