Skip to content

Instantly share code, notes, and snippets.

@lukemorton
Last active October 21, 2015 08:35
Show Gist options
  • Save lukemorton/19b68d1ead41602c207f to your computer and use it in GitHub Desktop.
Save lukemorton/19b68d1ead41602c207f to your computer and use it in GitHub Desktop.
falcor like stuff for ruby
# Designing a data structure spec
module Schema
Product = Typed::Hash[name: String,
url: String,
properties: Typed::Array[Property]]
Property = Typed::Hash[name: String, value: Typed::Any]
ProductList = Typed::Array[Product]
LineItem = Typed::Hash[product: Product, qty: Integer]
Cart = Typed::Hash[line_items: Typed::Array[LineItem]]
User = Typed::Hash[name: String]
end
# { "products": [{ "name": "Cool" }, { "name": "Bob" }] }
Route[:products, Typed::Array[Schema::Product]] do
def get
Product.first(20)
end
end
# { "product_by_pid": { "1": { "name": "Cool" } }}
Route[:product_by_pid, Arg[:pid], Schema::Product] do
def get(pid)
Product.find_by(pid: pid)
end
def set(pid, product_attrs)
Product.find_by(pid: pid).update!(product_attrs)
rescue ActiveRecord::RecordInvalid => e
errors(e.record.errors.messages)
end
end
# { "product_by_pid": { "1": { "add_to_cart": function (user_id, qty) {} } }}
Route[:product_by_pid, Arg[:pid], :add_to_cart] do
def call(pid, user_id, qty)
User.find(user_id).cart.add_product(Product.find_by(pid: pid), qty)
invalidate([:user, user_id, :cart])
end
end
# { "user": { "1": { "name": "Luke" } } }
Route[:user, Arg[:user_id], Schema::User] do
def get(user_id)
User.find(user_id)
end
end
# { "user": { "1": { "cart": { "line_items": [{ "product": {}, "qty": 1 }] } } } }
Route[:user, Arg[:user_id], :cart, Schema::Cart] do
def get(user_id)
User.find(user_id).cart
end
end
model = Router.build_model_from_routes(Route.all)
model.get([:user, 1, :cart]) # => Schema::Cart
model.set([:product_by_pid, 1], name: 'Nice product')
model.to_json # => {}
model.call([:product_by_pid, 1, :add_to_card], [1, 1])
class Route
def self.registered_routes
@routes ||= []
end
def self.replace_registered_route(original, replacement)
index = Route.registered_routes.find_index { |route_class| route_class == original }
Route.registered_routes[index] = replacement
end
def self.[](*args)
Class.new(Route).tap do |route_class|
Route.registered_routes << route_class
end
end
def self.define(&block)
class_eval(&block)
self
end
def self.inherited(subclass)
if self != Route
replace_registered_route(self, subclass)
end
end
end
class Arg < Struct.new(:name)
def self.[](name)
new(name)
end
end
# Block style definition
#
Route[:products].define do
def get
[:product_1, :product_2]
end
end
# Class based definition
#
class ProductByPidRoute < Route[:product_by_pid, Arg[:pid]]
def get(pid)
[:product_with_pid]
end
end
# Class override
#
# By extending any route you are effectively saying this new implmentation
# should be used instead.
#
class OverriddenProductByPidRoute < ProductByPidRoute
end
p Route.registered_routes.each do |route_class|
p route_class.new.respond_to?(:get)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment