380 lines
11 KiB
Ruby
Executable File
380 lines
11 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
|
|
%w{ rubygems sinatra mysql active_record yaml haml sass }.each do |lib|
|
|
require lib
|
|
end
|
|
|
|
configure do
|
|
# add your models into this variable in your initialization file
|
|
@@constants = []
|
|
# add the columns you want to search on into this variable
|
|
@@sortable_columns = []
|
|
|
|
# load up what you want to do in the app
|
|
init_file = File.join(File.dirname(__FILE__), 'one_man.rb')
|
|
require init_file if File.exists?(init_file)
|
|
|
|
# connect to the database
|
|
begin
|
|
dbf = File.join(File.dirname(__FILE__), 'config', 'database.yml')
|
|
dbconfig = YAML::load_file(dbf)[Sinatra::Application.environment.to_s]
|
|
ActiveRecord::Base.establish_connection(dbconfig)
|
|
rescue => exception
|
|
$stderr.puts "There was a problem connecting to the database:"
|
|
$stderr.puts "* #{exception.message}"
|
|
exception.backtrace.each do |msg|
|
|
$stderr.puts "-> #{msg}"
|
|
end
|
|
exit 1
|
|
end
|
|
|
|
@@constants.sort!
|
|
end
|
|
|
|
configure :development do
|
|
set :logging, true
|
|
ActiveRecord::Base.logger = Logger.new($stderr)
|
|
end
|
|
|
|
configure :production do
|
|
log = File.new(File.join(File.dirname(__FILE__), "thunderdome.log"), "a")
|
|
$stdout.reopen(log)
|
|
$stderr.reopen(log)
|
|
ActiveRecord::Base.logger = Logger.new(log)
|
|
end
|
|
|
|
get '/' do
|
|
haml :index
|
|
end
|
|
|
|
get '/stylesheet.css' do
|
|
content_type 'text/css', :charset => 'utf-8'
|
|
sass :stylesheet
|
|
end
|
|
|
|
before do
|
|
if params[:klass] and !@@constants.include? params[:klass]
|
|
raise "Bad Constant!"
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
helpers do
|
|
def constant_form
|
|
haml :constant_form
|
|
end
|
|
def constant_url(pagenum)
|
|
"/#{params[:klass]}/#{pagenum.to_i}?q=#{params[:q]}"
|
|
end
|
|
end
|
|
|
|
def all_booleans_to_false
|
|
@constant.attributes.keys.each do |key|
|
|
col = @constant.column_for_attribute(key)
|
|
next unless col.type == :boolean
|
|
@constant.send("#{key}=", false)
|
|
end
|
|
end
|
|
|
|
get '/:klass/edit/:id' do
|
|
@klass = params[:klass].constantize
|
|
@constant = @klass.find params[:id]
|
|
haml :edit
|
|
end
|
|
|
|
put '/:klass/edit/:id' do
|
|
@klass = params[:klass].constantize
|
|
@constant = @klass.find params[:id]
|
|
all_booleans_to_false
|
|
params[:constant].each do |name, value|
|
|
params[:constant][name] = value.to_date if name =~ /_date$/
|
|
end
|
|
|
|
@constant.attributes = params[:constant]
|
|
if @constant.save
|
|
redirect "/#{params[:klass]}/#{params[:page].to_i}?q=#{params[:q]}"
|
|
else
|
|
haml :edit
|
|
end
|
|
end
|
|
|
|
get '/:klass/new' do
|
|
@klass = params[:klass].constantize
|
|
@constant = @klass.new
|
|
haml :edit
|
|
end
|
|
|
|
post '/:klass/new' do
|
|
@klass = params[:klass].constantize
|
|
@constant = @klass.new
|
|
all_booleans_to_false
|
|
@constant.attributes = params[:constant]
|
|
if @constant.save
|
|
redirect "/#{params[:klass]}/#{params[:page].to_i}?q=#{params[:q]}"
|
|
else
|
|
haml :edit
|
|
end
|
|
end
|
|
|
|
get '/:klass/:page' do
|
|
@klass = params[:klass].constantize
|
|
conds, vars = [], {}
|
|
if params[:q] and !params[:q].to_s.empty?
|
|
vars[:q] = "%#{params[:q]}%"
|
|
cols = @klass.columns.collect(&:name)
|
|
@@sortable_columns.each do |col|
|
|
conds << "#{col} LIKE :q" if cols.include? col
|
|
end
|
|
end
|
|
@max_pages = (@klass.count(:conditions => [ conds.join(' OR '), vars ]) / 100).ceil
|
|
page_order = @klass.respond_to?(:custom_order) ? @klass.custom_order : 'id ASC'
|
|
@constants = @klass.find :all,
|
|
:limit => 100,
|
|
:order => page_order,
|
|
:offset => (params[:page].to_i * 100),
|
|
:conditions => [ conds.join(' OR '), vars ]
|
|
haml :show
|
|
end
|
|
|
|
delete '/:klass/:id' do
|
|
@klass = params[:klass].constantize
|
|
@constant = @klass.find params[:id]
|
|
@constant.destroy
|
|
redirect "/#{params[:klass]}/#{params[:page].to_i}?q=#{params[:q]}"
|
|
end
|
|
|
|
use_in_file_templates!
|
|
|
|
__END__
|
|
|
|
@@ layout
|
|
%html
|
|
%head
|
|
%title ThunderDome
|
|
%link{ :rel => 'stylesheet', :href => '/stylesheet.css' }
|
|
%body
|
|
= yield
|
|
|
|
@@ index
|
|
#header
|
|
%h1
|
|
Welcome to the ThunderDome!
|
|
%span.version== The Dos!
|
|
|
|
#content
|
|
%ol
|
|
- @@constants.each do |c|
|
|
%li
|
|
%a{ :href => "/#{c}/0" }
|
|
= c
|
|
|
|
@@ show
|
|
#header
|
|
%h1= params[:klass].pluralize
|
|
|
|
#content
|
|
%div#navigation
|
|
%a{ :href => '/', :title => 'Home' } Home
|
|
|
|
|
%a{ :href => "new?page=#{params[:page].to_i}&q=#{params[:q]}" }== New #{params[:klass]}
|
|
- if @max_pages > 0
|
|
|
|
|
Jump to page:
|
|
- if params[:page].to_i > 0
|
|
%a{ :href => constant_url(0) } First
|
|
|
|
|
- if params[:page].to_i > 1
|
|
%a{ :href => constant_url(params[:page].to_i - 1) } Previous
|
|
|
|
|
%select{ :id => 'pagenum' }
|
|
- (0..@max_pages).each do |page_num|
|
|
- if page_num == params[:page].to_i
|
|
%option{ :value => page_num, :selected => 'selected' }= page_num
|
|
- else
|
|
%option{ :value => page_num }= page_num
|
|
%form{ :method => 'GET' }
|
|
%input{ :type => 'hidden', :name => 'q', :value => params[:q] }
|
|
%button{ :onclick => 'this.form.action = "/' + params[:klass] + '/" + document.getElementById("pagenum").value; this.form.submit(); return false' } Go
|
|
- if params[:page].to_i < @max_pages - 1
|
|
|
|
|
%a{ :href => constant_url(params[:page].to_i + 1) } Next
|
|
- if params[:page].to_i < @max_pages
|
|
|
|
|
%a{ :href => constant_url(@max_pages) } Last
|
|
|
|
|
%form{ :method => 'GET', :action => "/#{params[:klass]}/#{params[:page]}" }
|
|
%input{ :type => 'text', :name => 'q', :value => params[:q] }
|
|
%button{ :onclick => 'this.form.submit(); return false' } Filter
|
|
%form{ :method => 'GET', :action => "/#{params[:klass]}/0" }
|
|
%button{ :onclick => 'this.form.submit(); return false' } Reset
|
|
|
|
- unless @constants.nil? or @constants.empty?
|
|
- join_keys = []
|
|
- parents = {}
|
|
- @constants.first.class.reflect_on_all_associations.select { |x| x.macro == :belongs_to }.each { |x| join_keys << [(x.options[:foreign_key] || "#{x.name}_id"), (x.options[:class_name] || x.name.to_s.camelize)] ; parents[join_keys.last.last] = [] }
|
|
- join_keys = join_keys.sort_by { |x| x.first }
|
|
- sorted_keys = (@constants.first.attributes.keys.sort rescue []).reject! { |x| x == 'id' or join_keys.any? { |y| y.first == x } }
|
|
%table.collectionList{ :cellspacing => 0 }
|
|
%tr
|
|
%th{ :width => "45px" }
|
|
- join_keys.each do |jk|
|
|
%th= jk.first
|
|
- sorted_keys.each do |field|
|
|
%th= field
|
|
- @constants.each_with_index do |c, idx|
|
|
%tr{ :class => (idx % 2 == 0 ? 'even' : 'odd') }
|
|
%td
|
|
%a{ :href => "/#{params[:klass]}/edit/#{c.id}?page=#{params[:page].to_i}&q=#{params[:q]}" }
|
|
%img{ :src => '/images/document-save.png' }
|
|
%a{ :onclick => "if (confirm('Are you sure you want to delete this entry?')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;", :href => "/#{params[:klass]}/#{c.id}?page=#{params[:page]}&q=#{params[:q]}" }
|
|
%img{ :src => '/images/user-trash.png' }
|
|
- join_keys.each do |jk|
|
|
%td
|
|
- id = c.send(jk[0])
|
|
%abbr{ :title => ((parents[jk[1]][id] ||= jk[1].constantize.find(id)) rescue 'NOTFOUND') }= id
|
|
- sorted_keys.each do |field|
|
|
%td= c.send(field)
|
|
|
|
@@ edit
|
|
- unless @constant.errors.empty?
|
|
#errorExplanation
|
|
%p The model could not be saved:
|
|
%ul
|
|
- @constant.errors.each_full do |msg|
|
|
%li= msg
|
|
|
|
#header
|
|
%h1== #{@constant.new_record? ? 'Creating' : 'Editing'} #{@klass}
|
|
|
|
#content
|
|
#navigation
|
|
%a{ :href => '/' } Home
|
|
|
|
|
%a{ :href => constant_url(params[:page]) }= params[:klass].pluralize
|
|
|
|
- if @constant.new_record?
|
|
%form{ :action => "/#{params[:klass]}/new", :method => 'POST' }
|
|
= constant_form
|
|
- else
|
|
%form{ :action => "/#{params[:klass]}/edit/#{@constant.id}", :method => 'pOST' }
|
|
%input{ :type => 'hidden', :name => '_method', :value => 'PUT' }
|
|
= constant_form
|
|
|
|
@@ constant_form
|
|
%input{ :type => 'hidden', :name => 'page', :value => params[:page].to_i }
|
|
%input{ :type => 'hidden', :name => 'q', :value => params[:q] }
|
|
%table{ :cellspacing => 0 }
|
|
- join_keys = []
|
|
- @constant.class.reflect_on_all_associations.select{|x| x.macro == :belongs_to}.each do |association|
|
|
- column = association.options[:foreign_key] || "#{association.name}_id"
|
|
- join_keys << column
|
|
- kls = (association.options[:class_name] || association.name.to_s.camelize).constantize
|
|
%tr
|
|
%td{ :width => '25%', :align => 'right' }
|
|
%label{ :for => "constant[#{column}]" }= kls
|
|
%td
|
|
%select{ :name => "constant[#{column}]", :id => "constant[#{column}]" }
|
|
- (kls.for_select(true) rescue kls.for_select).each do |fkname, fkid|
|
|
- if @constant.send(column).to_s == fkid
|
|
%option{ :value => fkid, :selected => 'selected' }= fkname
|
|
- else
|
|
%option{ :value => fkid }= fkname
|
|
- @constant.attributes.keys.sort.each do |key|
|
|
- next if key == 'id' or join_keys.include? key
|
|
%tr
|
|
%td{ :width => '25%', :align => 'right' }
|
|
%label{ :for => "constant[#{key}]" }= key
|
|
%td
|
|
- col = @constant.column_for_attribute(key)
|
|
- if col.type == :boolean
|
|
%input{ :type => 'checkbox', :name => "constant[#{key}]", :id => "constant[#{key}]", :checked => @constant.send("#{key}?"), :value => '1' }
|
|
- elsif col.type == :date
|
|
%input{ :type => 'date', :name => "constant[#{key}]", :id => "constant[#{key}]", :value => @constant.send(key) }
|
|
- elsif col.type == :text
|
|
%textarea{ :name => "constant[#{key}]", :id => "constant[#{key}]", :rows => 10, :cols => 50 }= @constant.send(key)
|
|
- else
|
|
- flen = col.limit.nil? ? 30 : col.limit
|
|
%input{ :type => 'text', :size => (flen > 50 ? 50 : flen), :maxsize => flen, :name => "constant[#{key}]", :id => "constant[#{key}]", :value => @constant.send(key) }
|
|
%input{ :type => 'submit', :value => 'Save' }
|
|
|
|
@@ stylesheet
|
|
body
|
|
margin: 0
|
|
margin-bottom: 25px
|
|
padding: 0
|
|
background-color: #f0f0f0
|
|
font:
|
|
family: "Lucida Grande", "Bitstream Vera Sans", "Verdana"
|
|
size: 12px
|
|
color: #333
|
|
|
|
#content
|
|
background-color: white
|
|
border: 3px solid #aaa
|
|
border-top: none
|
|
width: 90%
|
|
margin-left: auto
|
|
margin-right: auto
|
|
margin-top: 15px
|
|
padding: 5px
|
|
|
|
#navigation
|
|
margin: 10px 25px
|
|
form
|
|
display: inline
|
|
|
|
ol
|
|
margin-left: 25px
|
|
|
|
li
|
|
&:selected
|
|
background: #FCC
|
|
|
|
a
|
|
color: #03c
|
|
&:hover
|
|
background-color: #03c
|
|
color: white
|
|
text-decoration: none
|
|
img
|
|
text-decoration: none
|
|
border: none
|
|
vertical-align: text-bottom
|
|
|
|
#header
|
|
margin-top: 10px
|
|
padding-left: 75px
|
|
padding-right: 30px
|
|
h1
|
|
margin: 0
|
|
.version
|
|
color: #888
|
|
font-size: 16px
|
|
|
|
.even
|
|
background-color: #bbbbbb
|
|
|
|
.odd
|
|
background-color: #dddddd
|
|
|
|
.collectionList
|
|
padding: 2px
|
|
width: 100%
|
|
font-size: 12px
|
|
th
|
|
text-align: center
|
|
border-bottom: 2px solid #aaa
|
|
td
|
|
padding: 5px
|
|
tr:hover
|
|
background-color: #fcffa2
|
|
|
|
#errorExplanation {
|
|
padding: 10px
|
|
margin: 10px 0px
|
|
border: 2px solid #aaa
|
|
font-style: italic
|
|
background-color: #d28f8f
|
|
color: #8b0000
|