quirkey-qadmin 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +5 -0
- data/LICENSE +20 -0
- data/Manifest.txt +20 -12
- data/PostInstall.txt +1 -1
- data/README.rdoc +4 -29
- data/Rakefile +5 -3
- data/lib/qadmin.rb +4 -2
- data/lib/qadmin/assets/form_builder.rb +36 -0
- data/lib/qadmin/configuration.rb +48 -0
- data/lib/qadmin/controller.rb +64 -34
- data/lib/qadmin/form_builder.rb +23 -0
- data/lib/qadmin/helper.rb +46 -15
- data/lib/qadmin/option_set.rb +48 -0
- data/lib/qadmin/overlay.rb +31 -0
- data/lib/qadmin/page_titles.rb +34 -0
- data/lib/qadmin/templates.rb +52 -0
- data/lib/qadmin/views/content_forms/_attachments_form.erb +0 -0
- data/lib/qadmin/views/content_forms/_photos_form.erb +4 -0
- data/lib/qadmin/views/content_forms/_videos_form.erb +0 -0
- data/lib/qadmin/views/default/edit.erb +12 -0
- data/lib/qadmin/views/default/index.erb +9 -0
- data/lib/qadmin/views/default/new.erb +12 -0
- data/lib/qadmin/views/default/show.erb +7 -0
- data/rails/init.rb +2 -1
- data/rails_generators/qadmin/qadmin_generator.rb +36 -19
- data/rails_generators/qadmin/templates/_form.html.erb +7 -0
- data/rails_generators/qadmin/templates/_instance.html.erb +8 -0
- data/rails_generators/qadmin/templates/controller.rb +1 -1
- data/rails_generators/qadmin/templates/functional_test.rb +102 -65
- data/rails_generators/qadmin/templates/images/{icon_up.gif → icon_asc.gif} +0 -0
- data/rails_generators/qadmin/templates/images/{icon_down.gif → icon_desc.gif} +0 -0
- data/rails_generators/qadmin/templates/layout.html.erb +45 -0
- data/test/test_helper.rb +8 -0
- data/test/test_qadmin_controller.rb +16 -6
- data/test/test_qadmin_generator.rb +17 -2
- data/test/test_qadmin_option_set.rb +86 -0
- metadata +46 -14
- data/rails_generators/qadmin/templates/shoulda_functional_test.rb +0 -33
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/script/txt2html +0 -71
data/lib/qadmin/helper.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Qadmin
|
2
2
|
module Helper
|
3
|
-
include ::Qadmin::Options
|
4
3
|
|
5
4
|
def fieldset(legend = nil, options = {}, &block)
|
6
|
-
concat(
|
7
|
-
|
8
|
-
|
5
|
+
concat(content_tag(:fieldset, options) do
|
6
|
+
html = ''
|
7
|
+
html << content_tag(:legend, legend) if legend
|
8
|
+
html << capture(&block)
|
9
|
+
html
|
9
10
|
end)
|
10
11
|
end
|
11
12
|
|
@@ -25,7 +26,8 @@ module Qadmin
|
|
25
26
|
:edit => link_to(image_tag('admin/icon_edit.png') + " Edit", general_link_attributes.merge(:action => 'edit', :id => obj)),
|
26
27
|
:show => link_to(image_tag('admin/icon_show.png') + " View", general_link_attributes.merge(:action => 'show', :id => obj)),
|
27
28
|
:destroy => link_to(image_tag('admin/icon_destroy.png') + " Delete", general_link_attributes.merge(:action => 'destroy', :id => obj), :confirm => 'Are you sure?', :method => :delete),
|
28
|
-
:ports => link_to(image_tag('admin/icon_export.png') + " Import/Export", general_link_attributes.merge(:action => 'ports'))
|
29
|
+
:ports => link_to(image_tag('admin/icon_export.png') + " Import/Export", general_link_attributes.merge(:action => 'ports')),
|
30
|
+
:export => link_to(image_tag('admin/icon_export.png') + " Export", general_link_attributes.merge(:action => 'export'))
|
29
31
|
}
|
30
32
|
|
31
33
|
control_sets = {
|
@@ -34,10 +36,11 @@ module Qadmin
|
|
34
36
|
:edit => [:index,:new,:show,:destroy],
|
35
37
|
:show => [:index,:new,:edit,:destroy]
|
36
38
|
}
|
37
|
-
|
38
|
-
control_set = (options[:
|
39
|
+
|
40
|
+
control_set = (options[:controls] || []).dup
|
41
|
+
control_set.unshift(control_sets[options[:for]]) if options[:for]
|
39
42
|
control_set << :ports if options[:ports]
|
40
|
-
controls =
|
43
|
+
controls = [control_set].flatten.collect {|c| control_links[c] }.compact
|
41
44
|
|
42
45
|
html = ""
|
43
46
|
html << %{<ul class="admin_controls">}
|
@@ -45,22 +48,41 @@ module Qadmin
|
|
45
48
|
if block_given?
|
46
49
|
html << capture(&block)
|
47
50
|
html << %{</ul>}
|
48
|
-
concat(html
|
51
|
+
concat(html)
|
49
52
|
else
|
50
53
|
html << %{</ul>}
|
51
54
|
html
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
55
|
-
def sortable_column_header(attribute_name, options = {})
|
56
|
-
|
58
|
+
def sortable_column_header(attribute_name, text = nil, options = {})
|
59
|
+
link_text = text || self.qadmin_configuration.column_headers[attribute_name] || attribute_name.to_s.humanize
|
60
|
+
return link_text unless qadmin_configuration.model_klass.can_query?
|
61
|
+
query_parser = model_restful_query_parser(options)
|
62
|
+
query_param = options[:query_param] || :query
|
63
|
+
logger.warn 'params:' + self.params[query_param].inspect
|
64
|
+
logger.warn 'parser:' + query_parser.inspect
|
65
|
+
sorting_this = query_parser.sort(attribute_name)
|
66
|
+
logger.warn "sorting #{attribute_name}:" + sorting_this.inspect
|
67
|
+
link_text << " #{image_tag("admin/icon_#{sorting_this.direction.downcase}.gif")}" if sorting_this
|
68
|
+
query_parser.set_sort(attribute_name, sorting_this ? sorting_this.next_direction : 'desc')
|
69
|
+
link_to link_text, self.params.dup.merge(query_param => query_parser.to_query_hash), :class => 'sortable_column_header'
|
70
|
+
end
|
71
|
+
|
72
|
+
def model_restful_query_parser(options = {})
|
73
|
+
query_param = options[:query_param] || :query
|
74
|
+
qadmin_configuration.model_klass.restful_query_parser(params[query_param], options)
|
57
75
|
end
|
58
76
|
|
59
|
-
|
60
77
|
def admin_table(collection, options = {})
|
61
78
|
html = '<table>'
|
62
79
|
html << '<tr>'
|
63
|
-
attributes = options[:attributes] ||
|
80
|
+
attributes = options[:attributes] || self.qadmin_configuration.display_columns
|
81
|
+
model_column_types = SuperHash.new
|
82
|
+
attributes.each do |attribute_name|
|
83
|
+
column = self.qadmin_configuration.model_klass.columns.detect {|c| c.name == attribute_name.to_s }
|
84
|
+
model_column_types[attribute_name] = column.type if column
|
85
|
+
end
|
64
86
|
attributes.each_with_index do |attribute, i|
|
65
87
|
html << (i == 0 ? '<th class="first_col">' : '<th>')
|
66
88
|
html << sortable_column_header(attribute)
|
@@ -75,10 +97,19 @@ module Qadmin
|
|
75
97
|
collection.each do |instance|
|
76
98
|
html << %{<tr id="#{dom_id(instance)}" #{alt_rows}>}
|
77
99
|
attributes.each_with_index do |attribute, i|
|
100
|
+
raw_value = instance.send(attribute)
|
101
|
+
value = case model_column_types[attribute]
|
102
|
+
when :boolean
|
103
|
+
yes?(raw_value)
|
104
|
+
when :text
|
105
|
+
truncate_words(raw_value, 10, ". . . #{link_to('More', send("#{model_instance_name}_path", instance))}")
|
106
|
+
else
|
107
|
+
h(raw_value)
|
108
|
+
end
|
78
109
|
if i == 0
|
79
|
-
html << %{<td class="first_col">#{link_to(
|
110
|
+
html << %{<td class="first_col">#{link_to(value, send("#{model_instance_name}_path", instance))}</td>}
|
80
111
|
else
|
81
|
-
html << %{<td>#{
|
112
|
+
html << %{<td>#{value}</td>}
|
82
113
|
end
|
83
114
|
end
|
84
115
|
html << %{<td>#{link_to(image_tag('admin/icon_show.png'), send("#{model_instance_name}_path", instance))}</td>}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Qadmin
|
2
|
+
class OptionSet < Array
|
3
|
+
|
4
|
+
def initialize(*args)
|
5
|
+
first = args.shift
|
6
|
+
if first.is_a?(Array)
|
7
|
+
self.default = first
|
8
|
+
options = args.shift
|
9
|
+
else
|
10
|
+
options = first
|
11
|
+
end
|
12
|
+
if options.is_a?(Hash)
|
13
|
+
options.each do |option, value|
|
14
|
+
self.send("#{option}=", value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def current
|
20
|
+
[self].flatten.dup
|
21
|
+
end
|
22
|
+
|
23
|
+
[:exclude, :only, :default].each do |meth|
|
24
|
+
class_eval <<-EOT
|
25
|
+
def #{meth}
|
26
|
+
@#{meth} ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def #{meth}=(#{meth})
|
30
|
+
@#{meth} = [#{meth}].flatten if #{meth}
|
31
|
+
reset_current
|
32
|
+
end
|
33
|
+
EOT
|
34
|
+
end
|
35
|
+
alias_method :except, :exclude
|
36
|
+
alias_method :except=, :exclude=
|
37
|
+
|
38
|
+
protected
|
39
|
+
def reset_current
|
40
|
+
new_current = if !only.empty?
|
41
|
+
only
|
42
|
+
else
|
43
|
+
default - exclude
|
44
|
+
end
|
45
|
+
self.replace(new_current)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Qadmin
|
2
|
+
module Overlay
|
3
|
+
|
4
|
+
def self.included(other)
|
5
|
+
other.module_eval do
|
6
|
+
helper_method :overlay?
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
def layout_or_overlay(options = {})
|
12
|
+
@_overlay = false
|
13
|
+
fallback_layout = options[:default] || 'admin'
|
14
|
+
if (options[:only] && !Array(options[:only]).include?(action_name)) || (options[:except] && Array(options[:except]).include?(action_name))
|
15
|
+
return fallback_layout
|
16
|
+
end
|
17
|
+
if params[:_overlay]
|
18
|
+
@_overlay = true
|
19
|
+
false
|
20
|
+
else
|
21
|
+
fallback_layout
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
def overlay?
|
27
|
+
@_overlay && @_overlay == true
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Qadmin
|
2
|
+
module PageTitles
|
3
|
+
|
4
|
+
def self.included(other)
|
5
|
+
other.module_eval do
|
6
|
+
include ControllerMethods
|
7
|
+
extend MacroMethods
|
8
|
+
helper_method :get_page_title, :set_page_title
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module MacroMethods
|
13
|
+
def page_title(add_title)
|
14
|
+
before_filter do |controller|
|
15
|
+
controller.set_page_title(add_title)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ControllerMethods
|
21
|
+
|
22
|
+
def get_page_title(delimeter = ' : ')
|
23
|
+
@page_title ||= []
|
24
|
+
@page_title.join(delimeter)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def set_page_title(add_this)
|
29
|
+
get_page_title
|
30
|
+
@page_title << "#{add_this}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Qadmin
|
2
|
+
module Templates
|
3
|
+
|
4
|
+
def self.included(klass)
|
5
|
+
if klass.respond_to?(:helper_method)
|
6
|
+
klass.module_eval do
|
7
|
+
helper_method :template_for_section, :partial_for_section
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
def render_template_for_section(action = nil, options = {})
|
14
|
+
action ||= action_name
|
15
|
+
render({:template => template_for_section(action)}.merge(options))
|
16
|
+
end
|
17
|
+
|
18
|
+
def template_for_section(template_name, file_name = nil, options = {})
|
19
|
+
file_name ||= template_name
|
20
|
+
section_specific_template?(file_name, options) ? section_specific_template(template_name, options) : default_section_template(template_name, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def partial_for_section(partial_name, options = {})
|
24
|
+
template_for_section(partial_name, "_#{partial_name}", options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def section_specific_template?(template_name, options = {})
|
28
|
+
template_exists?(section_specific_template(template_name, options))
|
29
|
+
end
|
30
|
+
|
31
|
+
def section_specific_template(template_name, options = {})
|
32
|
+
"#{current_section_name(options)}/#{template_name}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_section_template(template_name, options = {})
|
36
|
+
"default/#{template_name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_section_name(options = {})
|
40
|
+
options[:section_name] ? options[:section_name] : (@section ? @section.name : controller_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def template_exists?(template_path)
|
44
|
+
logger.info "Checking for template: #{template_path}"
|
45
|
+
self.view_paths.find_template(template_path)
|
46
|
+
rescue ActionView::MissingTemplate
|
47
|
+
logger.info "Template not found: #{template_path}"
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<h2><%= model_human_name %>: Edit</h2>
|
2
|
+
|
3
|
+
<%= admin_controls model_collection_name, :for => :edit %>
|
4
|
+
|
5
|
+
<%= error_messages_for model_instance_name %>
|
6
|
+
|
7
|
+
<% form_for(@model_instance, :html => {:multipart => qadmin_configuration.multipart_forms}) do |f| %>
|
8
|
+
<%= render :partial => 'form', :locals => {:f => f, model_instance_name.to_sym => @model_instance} %>
|
9
|
+
<p><%= f.submit "Update" %></p>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<%= admin_controls model_collection_name, :for => :edit %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<h2><%= model_human_name.pluralize %></h2>
|
2
|
+
|
3
|
+
<%= admin_controls model_collection_name, :for => :index, :ports => qadmin_configuration.ports, :controls => qadmin_configuration.controls %>
|
4
|
+
|
5
|
+
<%= will_paginate(@model_collection) %>
|
6
|
+
<%= admin_table(@model_collection) %>
|
7
|
+
<%= will_paginate(@model_collection) %>
|
8
|
+
|
9
|
+
<%= admin_controls model_collection_name, :for => :index, :ports => qadmin_configuration.ports, :controls => qadmin_configuration.controls %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<h2><%= model_human_name %>: New</h2>
|
2
|
+
|
3
|
+
<%= admin_controls model_collection_name, :for => :new %>
|
4
|
+
|
5
|
+
<%= error_messages_for model_instance_name %>
|
6
|
+
|
7
|
+
<% form_for(@model_instance, :html => {:multipart => qadmin_configuration.multipart_forms}) do |f| %>
|
8
|
+
<%= render :partial => 'form', :locals => {:f => f, model_instance_name.to_sym => @model_instance} %>
|
9
|
+
<p><%= f.submit "Create" %></p>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<%= admin_controls model_collection_name, :for => :new %>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<h2><%= model_human_name.pluralize %>: <%= @model_instance.to_s %></h2>
|
2
|
+
|
3
|
+
<%= admin_controls model_collection_name, :for => :show, :object => @model_instance %>
|
4
|
+
|
5
|
+
<%= render :partial => model_instance_name, :object => @model_instance %>
|
6
|
+
|
7
|
+
<%= admin_controls model_collection_name, :for => :show, :object => @model_instance %>
|
data/rails/init.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'lib', 'qadmin')
|
2
2
|
|
3
3
|
ActionController::Base.send(:extend, Qadmin::Controller::Macros)
|
4
|
-
ActionView::Base.send(:include, Qadmin::Helper)
|
4
|
+
ActionView::Base.send(:include, Qadmin::Helper)
|
5
|
+
ActionView::Base.default_form_builder = Qadmin::FormBuilder
|
@@ -10,6 +10,8 @@ class QadminGenerator < Rails::Generator::NamedBase
|
|
10
10
|
alias_method :controller_file_name, :controller_singular_name
|
11
11
|
alias_method :controller_table_name, :controller_plural_name
|
12
12
|
|
13
|
+
attr_accessor :after_scaffold
|
14
|
+
|
13
15
|
def initialize(runtime_args, runtime_options = {})
|
14
16
|
super
|
15
17
|
|
@@ -23,6 +25,7 @@ class QadminGenerator < Rails::Generator::NamedBase
|
|
23
25
|
else
|
24
26
|
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
25
27
|
end
|
28
|
+
extract_options
|
26
29
|
end
|
27
30
|
|
28
31
|
def manifest
|
@@ -32,35 +35,41 @@ class QadminGenerator < Rails::Generator::NamedBase
|
|
32
35
|
m.class_collisions(controller_class_path, "#{controller_class_name}Controller")
|
33
36
|
|
34
37
|
# Controller, helper, views, and test directories.
|
35
|
-
|
36
|
-
|
38
|
+
if !after_scaffold
|
39
|
+
m.directory(File.join('app','controllers', controller_class_path))
|
40
|
+
m.directory(File.join('app', 'helpers', controller_class_path))
|
41
|
+
m.directory(File.join('public','images','admin'))
|
42
|
+
end
|
43
|
+
|
37
44
|
m.directory(File.join('app', 'views', controller_class_path, controller_file_name))
|
38
45
|
m.directory(File.join('test', 'functional', controller_class_path))
|
39
|
-
m.directory(File.join('public','images','admin'))
|
40
46
|
|
41
47
|
#copy form over too
|
42
|
-
m.template("_form.erb",File.join('app','views', controller_class_path, controller_file_name, "_form.erb"))
|
48
|
+
m.template("_form.html.erb",File.join('app','views', controller_class_path, controller_file_name, "_form.html.erb"))
|
49
|
+
m.template("_instance.html.erb", File.join('app','views', controller_class_path, controller_file_name, "_#{file_name}.html.erb"))
|
43
50
|
|
44
|
-
|
45
|
-
|
46
|
-
|
51
|
+
if !after_scaffold
|
52
|
+
# Layout and stylesheet.
|
53
|
+
m.template('layout.html.erb', File.join('app','views','layouts', "admin.erb"))
|
54
|
+
m.template('style.css', 'public/stylesheets/admin.css')
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
m.template('shoulda_functional_test.rb', File.join('test','functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
|
56
|
+
m.template('controller.rb', File.join('app','controllers', controller_class_path, "#{controller_file_name}_controller.rb"))
|
57
|
+
end
|
58
|
+
|
59
|
+
m.template('functional_test.rb', File.join('test','functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
|
53
60
|
|
54
|
-
|
61
|
+
if !after_scaffold
|
62
|
+
m.route_resources controller_file_name
|
55
63
|
|
56
|
-
|
57
|
-
|
58
|
-
|
64
|
+
# Copy Icons
|
65
|
+
Dir[File.join(File.dirname(__FILE__),'templates','images/*')].each do |image|
|
66
|
+
m.file File.join('images',File.basename(image)), File.join('public','images','admin',File.basename(image))
|
67
|
+
end
|
59
68
|
end
|
60
|
-
|
69
|
+
|
61
70
|
end
|
62
71
|
end
|
63
|
-
|
72
|
+
|
64
73
|
protected
|
65
74
|
# Override with your own usage banner.
|
66
75
|
def banner
|
@@ -71,7 +80,7 @@ class QadminGenerator < Rails::Generator::NamedBase
|
|
71
80
|
%w[ index show new edit ]
|
72
81
|
end
|
73
82
|
|
74
|
-
def model_name
|
83
|
+
def model_name
|
75
84
|
class_name.demodulize
|
76
85
|
end
|
77
86
|
|
@@ -91,4 +100,12 @@ class QadminGenerator < Rails::Generator::NamedBase
|
|
91
100
|
def ignore_attributes
|
92
101
|
%w{created_at updated_at}.push klass.protected_attributes
|
93
102
|
end
|
103
|
+
|
104
|
+
def add_options!(opts)
|
105
|
+
opts.on('--after-scaffold', "For use after generating a scaffold, generates the _form and the functional test") {|o| options[:after_scaffold] = o }
|
106
|
+
end
|
107
|
+
|
108
|
+
def extract_options
|
109
|
+
self.after_scaffold = options[:after_scaffold]
|
110
|
+
end
|
94
111
|
end
|