admin_assistant 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ class AdminAssistant
2
+ module Helper
3
+ def admin_assistant_includes
4
+ stylesheet_link_tag 'admin_assistant'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,181 @@
1
+ require 'ar_query'
2
+
3
+ class AdminAssistant
4
+ class Index
5
+ def initialize(admin_assistant, url_params = {})
6
+ @admin_assistant = admin_assistant
7
+ @url_params = url_params
8
+ end
9
+
10
+ def belongs_to_sort_column
11
+ columns.detect { |column|
12
+ column.is_a?(BelongsToColumn) && column.name.to_s == @url_params[:sort]
13
+ }
14
+ end
15
+
16
+ def columns
17
+ column_names = settings.column_names || model_class.columns.map { |c|
18
+ @admin_assistant.column_name_or_assoc_name(c.name)
19
+ }
20
+ @admin_assistant.columns column_names
21
+ end
22
+
23
+ def conditions
24
+ settings.conditions
25
+ end
26
+
27
+ def find_include
28
+ if by_assoc = belongs_to_sort_column
29
+ by_assoc.name
30
+ end
31
+ end
32
+
33
+ def model_class
34
+ @admin_assistant.model_class
35
+ end
36
+
37
+ def order_sql
38
+ if (sc = sort_column)
39
+ first_part = if (by_assoc = belongs_to_sort_column)
40
+ by_assoc.order_sql_field
41
+ else
42
+ sc.name
43
+ end
44
+ "#{first_part} #{sort_order}"
45
+ else
46
+ settings.sort_by
47
+ end
48
+ end
49
+
50
+ def records
51
+ unless @records
52
+ ar_query = ARQuery.new(
53
+ :order => order_sql, :include => find_include,
54
+ :per_page => 25, :page => @url_params[:page]
55
+ )
56
+ search.add_to_query(ar_query)
57
+ if conditions
58
+ conditions_sql = conditions.call @url_params
59
+ ar_query.condition_sqls << conditions_sql if conditions_sql
60
+ end
61
+ if settings.total_entries
62
+ ar_query[:total_entries] = settings.total_entries.call
63
+ end
64
+ @records = model_class.paginate :all, ar_query
65
+ end
66
+ @records
67
+ end
68
+
69
+ def search
70
+ @search ||= Search.new(@admin_assistant, @url_params['search'])
71
+ end
72
+
73
+ def search_terms
74
+ @url_params['search']
75
+ end
76
+
77
+ def settings
78
+ @admin_assistant.index_settings
79
+ end
80
+
81
+ def sort
82
+ @url_params[:sort]
83
+ end
84
+
85
+ def sort_column
86
+ if @url_params[:sort]
87
+ columns.detect { |c|
88
+ c.name.to_s == @url_params[:sort]
89
+ } || belongs_to_sort_column
90
+ end
91
+ end
92
+
93
+ def sort_order
94
+ @url_params[:sort_order] || 'asc'
95
+ end
96
+
97
+ def view(action_view)
98
+ View.new self, action_view
99
+ end
100
+
101
+ class Search
102
+ def initialize(admin_assistant, search_params)
103
+ @admin_assistant, @search_params = admin_assistant, search_params
104
+ @search_params ||= {}
105
+ end
106
+
107
+ def [](name)
108
+ @search_params[name]
109
+ end
110
+
111
+ def add_to_query(ar_query)
112
+ columns.each do |column|
113
+ column.add_to_query ar_query
114
+ end
115
+ end
116
+
117
+ def columns
118
+ search_field_names = settings.search_fields
119
+ if search_field_names.empty?
120
+ [DefaultSearchColumn.new(
121
+ default_terms, @admin_assistant.model_class
122
+ )]
123
+ else
124
+ columns = search_field_names.map { |column_name|
125
+ @admin_assistant.column column_name.to_s
126
+ }
127
+ columns.each do |c|
128
+ c.search_terms = @search_params[c.name]
129
+ end
130
+ columns
131
+ end
132
+ end
133
+
134
+ def column_views(action_view)
135
+ columns.map { |c|
136
+ opts = {:search => self}
137
+ if c.respond_to?(:name)
138
+ opts[:boolean_labels] = settings.boolean_labels[c.name]
139
+ end
140
+ c.view(action_view, opts)
141
+ }
142
+ end
143
+
144
+ def default_terms
145
+ @search_params if @search_params.is_a?(String)
146
+ end
147
+
148
+ def id
149
+ end
150
+
151
+ def method_missing(meth, *args)
152
+ if column = columns.detect { |c| c.name == meth.to_s }
153
+ column.search_value
154
+ else
155
+ super
156
+ end
157
+ end
158
+
159
+ def settings
160
+ @admin_assistant.index_settings
161
+ end
162
+ end
163
+
164
+ class View
165
+ def initialize(index, action_view)
166
+ @index, @action_view = index, action_view
167
+ end
168
+
169
+ def columns
170
+ @index.columns.map { |c|
171
+ c.view(
172
+ @action_view,
173
+ :boolean_labels => @index.settings.boolean_labels[c.name],
174
+ :sort_order => (@index.sort_order if c.name == @index.sort),
175
+ :link_to_args => @index.settings.link_to_args[c.name.to_sym]
176
+ )
177
+ }
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,183 @@
1
+ class AdminAssistant
2
+ module Request
3
+ class Base
4
+ def initialize(admin_assistant, controller)
5
+ @admin_assistant, @controller = admin_assistant, controller
6
+ end
7
+
8
+ def action
9
+ @controller.action_name
10
+ end
11
+
12
+ def after_form_html_template
13
+ File.join(
14
+ RAILS_ROOT, 'app/views/', @controller.controller_path,
15
+ '_after_form.html.erb'
16
+ )
17
+ end
18
+
19
+ def after_form_html_template_exists?
20
+ File.exist? after_form_html_template
21
+ end
22
+
23
+ def model_class
24
+ @admin_assistant.model_class
25
+ end
26
+
27
+ def model_class_symbol
28
+ model_class.name.underscore.to_sym
29
+ end
30
+
31
+ def params_for_save
32
+ params = {}
33
+ split_params = {}
34
+ whole_params = {}
35
+ @controller.params[model_class_symbol].each do |k, v|
36
+ k =~ /\([0-9]+i\)$/ ? (split_params[k] = v) : (whole_params[k] = v)
37
+ end
38
+ bases = split_params.map{ |k, v| k.gsub(/\([0-9]+i\)$/, '') }.uniq
39
+ bases.each do |b|
40
+ h = {}
41
+ split_params.each{ |k, v| h[k] = split_params.delete(k) if k =~ /#{b}\([0-9]+i\)$/ }
42
+ from_form_method = "#{b}_from_form".to_sym
43
+ if @controller.respond_to?(from_form_method)
44
+ params[b] = @controller.send(from_form_method, h)
45
+ elsif @record.respond_to?("#{b}=")
46
+ params.merge! h
47
+ end
48
+ end
49
+ whole_params.each do |k, v|
50
+ from_form_method = "#{k}_from_form".to_sym
51
+ if @controller.respond_to?(from_form_method)
52
+ params[k] = @controller.send(from_form_method, v)
53
+ elsif @record.respond_to?("#{k}=")
54
+ params[k] = v
55
+ end
56
+ end
57
+ params
58
+ end
59
+
60
+ def redirect_after_save
61
+ url_params = if @controller.respond_to?(:destination_after_save)
62
+ @controller.send(
63
+ :destination_after_save, @record, @controller.params
64
+ )
65
+ end
66
+ url_params ||= {:action => 'index'}
67
+ @controller.send :redirect_to, url_params
68
+ end
69
+
70
+ def render_after_form
71
+ @controller.send(
72
+ :render_to_string,
73
+ :file => after_form_html_template, :layout => false
74
+ )
75
+ end
76
+
77
+ def render_form
78
+ html = @controller.send(
79
+ :render_to_string, :file => template_file('form'), :layout => true
80
+ )
81
+ html << render_after_form if after_form_html_template_exists?
82
+ @controller.send :render, :text => html
83
+ end
84
+
85
+ def render_template_file(template_name = action, options_plus = {})
86
+ options = {:file => template_file(template_name), :layout => true}
87
+ options = options.merge options_plus
88
+ @controller.send(:render, options)
89
+ end
90
+
91
+ def save
92
+ if @controller.respond_to?(:before_save)
93
+ @controller.send(:before_save, @record)
94
+ end
95
+ @record.save
96
+ end
97
+
98
+ def template_file(template_name = action)
99
+ "#{File.dirname(__FILE__)}/../views/#{template_name}.html.erb"
100
+ end
101
+ end
102
+
103
+ class Create < Base
104
+ def call
105
+ @record = model_class.new
106
+ @record.attributes = params_for_save
107
+ if save
108
+ redirect_after_save
109
+ else
110
+ @controller.instance_variable_set :@record, @record
111
+ render_form
112
+ end
113
+ end
114
+
115
+ def save
116
+ if @controller.respond_to?(:before_create)
117
+ @controller.send(:before_create, @record)
118
+ end
119
+ result = super
120
+ if @controller.respond_to?(:after_create)
121
+ @controller.send(:after_create, @record)
122
+ end
123
+ result
124
+ end
125
+ end
126
+
127
+ class Destroy < Base
128
+ def call
129
+ @record = model_class.find @controller.params[:id]
130
+ @record.destroy
131
+ @controller.send :render, :text => ''
132
+ end
133
+ end
134
+
135
+ class Edit < Base
136
+ def call
137
+ @record = model_class.find @controller.params[:id]
138
+ @controller.instance_variable_set :@record, @record
139
+ render_form
140
+ end
141
+ end
142
+
143
+ class Index < Base
144
+ def call
145
+ index = AdminAssistant::Index.new(@admin_assistant, @controller.params)
146
+ @controller.instance_variable_set :@index, index
147
+ render_template_file
148
+ end
149
+
150
+ def columns
151
+ @admin_assistant.index_settings.columns
152
+ end
153
+ end
154
+
155
+ class New < Base
156
+ def call
157
+ @record = model_class.new
158
+ @controller.instance_variable_set :@record, @record
159
+ render_form
160
+ end
161
+ end
162
+
163
+ class Update < Base
164
+ def call
165
+ @record = model_class.find @controller.params[:id]
166
+ @record.attributes = params_for_save
167
+ if save
168
+ redirect_after_save
169
+ else
170
+ @controller.instance_variable_set :@record, @record
171
+ render_form
172
+ end
173
+ end
174
+
175
+ def save
176
+ if @controller.respond_to?(:before_update)
177
+ @controller.send(:before_update, @record)
178
+ end
179
+ super
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,172 @@
1
+ require 'admin_assistant/builder'
2
+ require 'admin_assistant/column'
3
+ require 'admin_assistant/form_view'
4
+ require 'admin_assistant/helper'
5
+ require 'admin_assistant/index'
6
+ require 'admin_assistant/request'
7
+ require 'will_paginate'
8
+
9
+ class AdminAssistant
10
+ attr_reader :custom_column_labels, :form_settings, :index_settings,
11
+ :model_class
12
+ attr_accessor :actions
13
+
14
+ def initialize(controller_class, model_class)
15
+ @controller_class, @model_class = controller_class, model_class
16
+ @actions = [:index, :create, :update]
17
+ @form_settings = FormSettings.new self
18
+ @index_settings = IndexSettings.new self
19
+ @custom_column_labels = {}
20
+ end
21
+
22
+ def belongs_to_assoc(name)
23
+ model_class.reflect_on_all_associations.detect { |assoc|
24
+ assoc.macro == :belongs_to && assoc.name.to_s == name.to_s
25
+ }
26
+ end
27
+
28
+ def column(name)
29
+ column = if file_columns.include?(name)
30
+ FileColumnColumn.new name
31
+ elsif (ar_column = model_class.columns_hash[name.to_s])
32
+ ActiveRecordColumn.new(ar_column)
33
+ elsif belongs_to_assoc = belongs_to_assoc(name)
34
+ BelongsToColumn.new(belongs_to_assoc)
35
+ else
36
+ AdminAssistantColumn.new(name)
37
+ end
38
+ if column && (custom = custom_column_labels[name.to_s])
39
+ column.custom_label = custom
40
+ end
41
+ column
42
+ end
43
+
44
+ def column_name_or_assoc_name(name)
45
+ result = name
46
+ ar_column = model_class.columns_hash[name.to_s]
47
+ if ar_column
48
+ associations = model_class.reflect_on_all_associations
49
+ if belongs_to_assoc = associations.detect { |assoc|
50
+ assoc.macro == :belongs_to && assoc.association_foreign_key == name
51
+ }
52
+ result = belongs_to_assoc.name.to_s
53
+ end
54
+ end
55
+ result
56
+ end
57
+
58
+ def columns(names)
59
+ columns = paperclip_attachments.map { |paperclip_attachment|
60
+ PaperclipColumn.new paperclip_attachment
61
+ }
62
+ names.each do |column_name|
63
+ unless columns.any? { |column| column.contains?(column_name) }
64
+ column = column column_name
65
+ columns << column if column
66
+ end
67
+ end
68
+ columns
69
+ end
70
+
71
+ def controller_actions
72
+ c_actions = actions.clone
73
+ c_actions << :new if c_actions.include?(:create)
74
+ c_actions << :edit if c_actions.include?(:update)
75
+ c_actions
76
+ end
77
+
78
+ def controller_css_class(controller)
79
+ controller.controller_path.gsub(%r|/|, '_')
80
+ end
81
+
82
+ def dispatch_to_request_method(request_method, controller)
83
+ controller.instance_variable_set :@admin_assistant, self
84
+ klass = Request.const_get request_method.to_s.capitalize
85
+ @request = klass.new(self, controller)
86
+ @request.call
87
+ @request = nil
88
+ end
89
+
90
+ def file_columns
91
+ fc = []
92
+ if model_class.respond_to?(:file_column)
93
+ model_class.columns.each do |column|
94
+ suffixes = %w( relative_path dir relative_dir temp )
95
+ if suffixes.all? { |suffix|
96
+ model_class.method_defined? "#{column.name}_#{suffix}".to_sym
97
+ }
98
+ fc << column.name
99
+ end
100
+ end
101
+ end
102
+ fc
103
+ end
104
+
105
+ def method_missing(meth, *args)
106
+ request_methods = [:create, :destroy, :edit, :index, :new, :update]
107
+ if request_methods.include?(meth) and args.size == 1
108
+ dispatch_to_request_method meth, args.first
109
+ else
110
+ if meth.to_s =~ /(.*)\?/ && request_methods.include?($1.to_sym)
111
+ @actions.include?($1.to_sym)
112
+ elsif @request.respond_to?(meth)
113
+ @request.send meth, *args
114
+ else
115
+ super
116
+ end
117
+ end
118
+ end
119
+
120
+ def model_class_name
121
+ model_class.name.gsub(/([A-Z])/, ' \1')[1..-1].downcase
122
+ end
123
+
124
+ def paperclip_attachments
125
+ pa = []
126
+ if model_class.respond_to?(:attachment_definitions)
127
+ if model_class.attachment_definitions
128
+ pa = model_class.attachment_definitions.map { |name, definition|
129
+ name
130
+ }
131
+ end
132
+ end
133
+ pa
134
+ end
135
+
136
+ def url_params(a = action)
137
+ {:controller => @controller_class.controller_path, :action => a}
138
+ end
139
+
140
+ module ControllerMethods
141
+ def self.included(controller)
142
+ controller.extend ControllerClassMethods
143
+ controller.class_inheritable_accessor :admin_assistant
144
+ end
145
+ end
146
+
147
+ module ControllerClassMethods
148
+ def admin_assistant_for(model_class, &block)
149
+ self.admin_assistant = AdminAssistant.new(self, model_class)
150
+ builder = Builder.new self.admin_assistant
151
+ if block
152
+ block.call builder
153
+ end
154
+ self.helper AdminAssistant::Helper
155
+ self.admin_assistant.controller_actions.each do |action|
156
+ self.send(:define_method, action) do
157
+ self.class.admin_assistant.send(action, self)
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ ActionController::Base.send :include, AdminAssistant::ControllerMethods
165
+
166
+ FileUtils.copy(
167
+ "#{File.dirname(__FILE__)}/stylesheets/admin_assistant.css",
168
+ "#{RAILS_ROOT}/public/stylesheets/admin_assistant.css"
169
+ )
170
+ images_dir = "#{RAILS_ROOT}/public/images/admin_assistant"
171
+ FileUtils.mkdir(images_dir) unless File.exist?(images_dir)
172
+ FileUtils.cp_r(Dir.glob("#{File.dirname(__FILE__)}/images/*"), images_dir)
Binary file
Binary file
@@ -0,0 +1,75 @@
1
+ /******************************************************************************
2
+ Don't edit this file: It gets re-copied every time the server starts.
3
+ ******************************************************************************/
4
+
5
+ #admin_assistant { font-family: "Verdana","Arial", sans-serif }
6
+ #admin_assistant h2 { font-size: large; font-weight: bold }
7
+ #admin_assistant .actions {
8
+ position: absolute; right: 5px; top: 5px; text-align: right
9
+ }
10
+ #admin_assistant #header { position: relative }
11
+
12
+ /***** Index stuff ***********************************************************/
13
+
14
+ #admin_assistant.index table {
15
+ width: 100%; margin: 0px 0px 10px 0px; padding: 0px
16
+ }
17
+ #admin_assistant.index th {
18
+ background: #666; color: white; padding: 5px 20px 5px 5px
19
+ }
20
+ #admin_assistant.index th a {
21
+ color: white
22
+ }
23
+ #admin_assistant.index th.sort.asc {
24
+ background: #000 url(/images/admin_assistant/sort-asc.png) no-repeat right
25
+ }
26
+ #admin_assistant.index th.sort.desc {
27
+ background: #000 url(/images/admin_assistant/sort-desc.png) no-repeat right
28
+ }
29
+ #admin_assistant.index td {
30
+ background: white;
31
+ border-bottom: 1px solid #ccc;
32
+ border-right: 1px solid #ccc;
33
+ padding: 5px
34
+ }
35
+ #admin_assistant.index tbody tr td:first-child {
36
+ border-left: 1px solid #ccc;
37
+ }
38
+ #admin_assistant.index tbody tr.even td {
39
+ background: #eee;
40
+ }
41
+
42
+ #admin_assistant.index tbody tr td.sort {
43
+ background: #ddd
44
+ }
45
+ #admin_assistant.index tbody tr.even td.sort {
46
+ background: #ccc
47
+ }
48
+
49
+ /***** Form stuff ************************************************************/
50
+
51
+ #admin_assistant.form .column {
52
+ margin-bottom: 10px;
53
+ }
54
+
55
+ #admin_assistant.form label {
56
+ /* font-size: small;*/
57
+ }
58
+
59
+ #admin_assistant.form input {
60
+ font-size: large
61
+ }
62
+
63
+ #admin_assistant.form input[type=text] {
64
+ width: 100%
65
+ }
66
+
67
+ #admin_assistant.form select {
68
+ font-size: large
69
+ }
70
+
71
+ #admin_assistant.form textarea {
72
+ width: 100%;
73
+ font-size: large
74
+ }
75
+
@@ -0,0 +1,31 @@
1
+ <%
2
+ form_view = AdminAssistant::FormView.new(@record, @admin_assistant, self)
3
+ %>
4
+ <div id="admin_assistant" class="form <%=
5
+ @admin_assistant.controller_css_class(controller)
6
+ %>">
7
+ <% id = nil unless defined?(id) %>
8
+ <div id="header">
9
+ <h2><%= h(form_view.title) %></h2>
10
+ <div class="actions">
11
+ <%= link_to('Back to index', :action => 'index') %>
12
+ </div>
13
+ </div>
14
+ <% if !@record.errors.empty? %>
15
+ <%= error_messages_for 'record' %>
16
+ <% end %>
17
+ <% form_for(@record, form_view.form_for_args) do |rails_form| %>
18
+ <% form_view.columns.each do |column| %>
19
+ <div class="column">
20
+ <label><%= h(column.label) %></label>
21
+ <br /><%= form_view.column_html(column, rails_form) %>
22
+ </div>
23
+ <% end %>
24
+ <div>
25
+ <%= submit_tag(form_view.submit_value) %>
26
+ <% form_view.extra_submit_buttons.each do |extra_submit_button| %>
27
+ <%= submit_tag(extra_submit_button) %>
28
+ <% end %>
29
+ </div>
30
+ <% end %>
31
+ </div>