Skip to content

Instantly share code, notes, and snippets.

@plexus
Created June 29, 2013 22:30
Show Gist options
  • Save plexus/5892947 to your computer and use it in GitHub Desktop.
Save plexus/5892947 to your computer and use it in GitHub Desktop.
require 'hexp'
class FormBuilder
# All the ones that Drupal's form API implements
# https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/7
# TYPES = %w[checkbox checkboxes date fieldset file machine_name managed_file
# password password_confirm radio radios select tableselect text_format
# textarea textfield vertical_tabs weight].map(&:to_sym)
TYPES = %w[select textfield].map(&:to_sym)
attr_reader :elements
def initialize(&blk)
@elements = []
instance_eval(&blk)
end
TYPES.each do |type|
define_method type do |name, opts = {}, &blk|
@elements << [type, name, opts]
end
end
def form_class
Class.new(Form).tap do |klz|
klz.elements = @elements
end
end
end
class Form
include Hexp
class << self
attr_accessor :elements
end
def self.build(&blk)
raise unless blk
FormBuilder.new(&blk).form_class
end
attr_reader :values
def initialize(model_or_params)
@values = Hash[
self.class.elements.map do |type, name, opts|
value = model_or_params[name]
options = element_opts(name)
if options
value = nil
options.each do |option_value, text|
if option_value.to_s == model_or_params[name].to_s
value = option_value
break
end
end
end
[name, value]
end
]
end
def element_opts(element_name)
self.class.elements.select do |type, name, opts|
element_name == name
end[2]
end
def to_hexp
H[:form, elements]
end
def elements
self.class.elements.map do |type, name, opts|
H[:div, [
H[:label, {for: name}, opts[:title]],
self.send(type, name, opts).attr('name', name)
]
]
end
end
def textfield(name, opts)
H[:input, {type: "text", value: @values[name]}]
end
def select(name, opts)
H[:select, opts[:options].map do |value, text|
H[:option, {value: value}, text]
end
]
end
def select_option(name, value, text)
attrs = {value: value}
attrs[:selected] = 'selected' if @value[name] == value
H[:option, attrs, text]
end
end
source 'https://rubygems.org'
gem 'hexp', path: '/home/arne/github/hexp'
gem 'virtus', github: 'solnic/virtus'
gem 'sinatra'
gem 'active_support'
gem 'debugger'
#!/usr/bin/env ruby
require 'hexp'
require 'virtus'
require 'sinatra'
require 'pstore'
require 'active_support/core_ext/hash/slice'
require_relative 'form_builder'
require_relative 'issue_repo'
class Issue
include Virtus
STATES = [:open, :closed]
PRIORITIES = {:high => "High", :medium => "Medium", :low => "Low"}
attribute :id, Integer
attribute :title, String
attribute :state, Symbol
attribute :priority, Symbol
def priority_str
PRIORITIES[priority]
end
end
IssueForm = Form.build do
textfield :title, title: "Title"
select :priority, title: "Priority",
options: Issue::PRIORITIES
end
class IssueList
include Hexp
attr_reader :issues
def initialize(issues)
@issues = issues
end
def to_hexp
H[:table, [
H[:thead, H[:tr, [ H[:th, "Title"], H[:th, "Priority"] ]]],
H[:tbody, issues.map {|issue| H[:tr, [ H[:td, issue.title], H[:td, issue.priority_str]]] }]
]
]
end
end
class App < Sinatra::Base
def repo
@repo ||= IssueRepo.new
end
get '/' do
begin
IssueList.new(repo.all).to_html
rescue
IssueRepo.new.all.inspect
end
end
get '/new' do
IssueForm.new(Issue.new).attrs(method: "POST", action: "/create").to_html
end
post '/create' do
form = IssueForm.new(params)
repo.store(Issue.new(form.values))
redirect "/"
end
end
App.run! if __FILE__ == $0
class IssueRepo
def repo
@repo ||= PStore.new('issues.pstore')
end
def find(id)
read { repo[id] }
end
def store(issue)
unless issue.id
issue.id = next_id
end
write { repo[issue.id] = issue }
end
def next_id
max = read { repo.roots }.max
(max || 0) + 1
end
def all
read do
repo.roots.map {|i| repo[i]}
end
end
private
def read(&blk)
repo.transaction true, &blk
end
def write(&blk)
repo.transaction &blk
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment