simple_admin 0.5.2 → 0.6.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/Gemfile CHANGED
@@ -3,10 +3,10 @@ source "http://rubygems.org"
3
3
  # Gem's dependencies in simple_admin.gemspec
4
4
  gemspec
5
5
 
6
- gem "rails", ">= 3.1.0rc4"
6
+ gem "rails", ">= 3.2.0"
7
7
  gem "kaminari", ">= 0"
8
8
  gem "formtastic", ">= 0"
9
- gem "meta_search", ">= 1.1.0.pre"
9
+ gem "ransack"
10
10
  gem "fastercsv" if RUBY_VERSION =~ /^1.8/
11
11
  gem "fuubar"
12
12
 
@@ -72,7 +72,7 @@ You can also specify to which actions the before block should be
72
72
  applied:
73
73
 
74
74
  SimpleAdmin.register :post do
75
- before :only => [:create, :delete] do
75
+ before :only => [:create, :destroy] do
76
76
  require_admin
77
77
  end
78
78
  end
@@ -219,7 +219,7 @@ generated at initialization. Available actions are:
219
219
  * new
220
220
  * show
221
221
  * edit
222
- * delete
222
+ * destroy
223
223
  * create
224
224
  * update
225
225
 
data/Rakefile CHANGED
@@ -44,8 +44,8 @@ task :sample do
44
44
  # If we are not on 1.9.x we need fastercsv
45
45
  system "echo 'gem \"fastercsv\"' >> Gemfile" if RUBY_VERSION =~ /^1.8/
46
46
 
47
- # To work with Rails 3.1.x we need the latest meta_search
48
- system "echo 'gem \"meta_search\", \">= 1.1.0.pre\"' >> Gemfile"
47
+ # To work with Rails 3.1.x we need ransack
48
+ system "echo 'gem \"ransack\"' >> Gemfile"
49
49
 
50
50
  # With more gems, comes more bundling
51
51
  system "bundle"
@@ -793,7 +793,7 @@ body {
793
793
  margin-right: 300px; }
794
794
  #active_admin_content.without_sidebar #main_content_wrapper #main_content {
795
795
  margin-right: 0; }
796
- #active_admin_content #sidebar {
796
+ #active_admin_content #sidebar, #active_admin_content #filters {
797
797
  float: left;
798
798
  width: 270px;
799
799
  margin-left: -270px; }
@@ -989,7 +989,7 @@ table {
989
989
  .panel_contents table tr.even td {
990
990
  background: #f4f5f5; }
991
991
 
992
- .sidebar_section {
992
+ .sidebar_section, .filters_section {
993
993
  background: #f4f4f4;
994
994
  -webkit-border-radius: 4px;
995
995
  -moz-border-radius: 4px;
@@ -998,7 +998,7 @@ table {
998
998
  -moz-box-shadow: inset 0 1px 4px #dddddd;
999
999
  -webkit-box-shadow: inset 0 1px 4px #dddddd;
1000
1000
  margin-bottom: 20px; }
1001
- .sidebar_section h3 {
1001
+ .sidebar_section h3, .filters_section h3 {
1002
1002
  background: #efefef;
1003
1003
  background: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#dfe1e2));
1004
1004
  background: -moz-linear-gradient(-90deg, #efefef, #dfe1e2);
@@ -1015,16 +1015,21 @@ table {
1015
1015
  line-height: 140%;
1016
1016
  margin-bottom: 0.5em;
1017
1017
  color: #5e6469; }
1018
+ .filters_section h3 span.icon svg path, .filters_section h3 span.icon svg polygon, .filters_section h3 span.icon svg rect, .filters_section h3 span.icon svg circle,
1018
1019
  .sidebar_section h3 span.icon svg path, .sidebar_section h3 span.icon svg polygon, .sidebar_section h3 span.icon svg rect, .sidebar_section h3 span.icon svg circle {
1019
1020
  fill: #5e6469 !important; }
1021
+ .filters_section h3 span.icon,
1020
1022
  .sidebar_section h3 span.icon {
1021
1023
  width: 1em;
1022
1024
  height: 1em; }
1025
+ .filters_section h3 span.icon svg,
1023
1026
  .sidebar_section h3 span.icon svg {
1024
1027
  width: 1em;
1025
1028
  height: 1em; }
1029
+ .filters_section h3 span.icon,
1026
1030
  .sidebar_section h3 span.icon {
1027
1031
  margin-right: 5px; }
1032
+ .filters_section > div,
1028
1033
  .sidebar_section > div {
1029
1034
  padding: 3px 15px 15px 15px; }
1030
1035
 
@@ -1385,6 +1390,7 @@ table.dashboard {
1385
1390
  text-transform: uppercase;
1386
1391
  letter-spacing: 0.2em; }
1387
1392
 
1393
+ .filters_section .attributes_table th,
1388
1394
  .sidebar_section .attributes_table th {
1389
1395
  width: 50px; }
1390
1396
 
@@ -1,5 +1,5 @@
1
1
  require 'kaminari'
2
- require 'meta_search'
2
+ require 'ransack'
3
3
 
4
4
  module SimpleAdmin
5
5
  class AdminController < ::ApplicationController
@@ -7,6 +7,7 @@ module SimpleAdmin
7
7
  before_filter :lookup_interface, :except => [:dashboard]
8
8
  before_filter :lookup_before, :except => [:dashboard]
9
9
  before_filter :lookup_resource, :only => [:show, :edit, :update, :destroy]
10
+ before_filter :check_action, :except => [:dashboard]
10
11
 
11
12
  unloadable
12
13
 
@@ -17,10 +18,12 @@ module SimpleAdmin
17
18
  layout 'simple_admin'
18
19
 
19
20
  def index
20
- @collection = @interface.constant
21
- @collection = @collection.order("#{@interface.constant.table_name}.#{$1} #{$2}") if params[:order] && params[:order] =~ /^([\w\_\.]+)_(desc|asc)$/
22
- @collection = @collection.metasearch(clean_search_params(params))
23
- @collection = @collection.page(params[:page]).per(params[:per_page] || SimpleAdmin.default_per_page) if params[:format].blank? || params[:format] == 'html'
21
+ @collection = @interface.constant rescue nil
22
+ if @collection
23
+ @collection = @collection.search(clean_search_params(params)).result
24
+ @collection = @collection.order("#{@interface.constant.table_name}.#{$1} #{$2}") if params[:order] && params[:order] =~ /^([\w\_\.]+)_(desc|asc)$/
25
+ @collection = @collection.page(params[:page]).per(params[:per_page] || SimpleAdmin.default_per_page) if params[:format].blank? || params[:format] == 'html'
26
+ end
24
27
  respond_to do |format|
25
28
  format.csv
26
29
  format.html
@@ -116,9 +119,13 @@ module SimpleAdmin
116
119
  search_params = search_params.dup
117
120
  search_params.delete_if do |key, value|
118
121
  value == "" ||
119
- ["utf8", "scope", "commit", "action", "order", "interface", "controller", "format", "page", "per_page"].include?(key)
122
+ ["utf8", "scope", "commit", "action", "order", "interface", "controller", "format", "page", "per_page", "editing", "mode"].include?(key)
120
123
  end
121
124
  search_params
122
125
  end
126
+
127
+ def check_action
128
+ raise SimpleAdmin::ActionNotAllowed.new(params[:action]) unless @interface.actions.include?(params[:action].to_sym)
129
+ end
123
130
  end
124
131
  end
@@ -1,10 +1,31 @@
1
1
  module SimpleAdmin
2
2
  module FilterHelper
3
+ def filter_fields(attributes)
4
+ attributes.map do |col|
5
+ options = col.options.dup
6
+ expand_block_options!(options)
7
+ case col.kind
8
+ when :attribute, :filter
9
+ filter_for(col.attribute, @interface.constant, options)
10
+ when :content
11
+ instance_exec(self, &col.data)
12
+ when :fieldset
13
+ content_tag :fieldset, options do
14
+ content_tag :legend do
15
+ options[:legend]
16
+ end unless options[:legend].blank
17
+ filter_fields(col.attributes)
18
+ end
19
+ else
20
+ content_tag :div, options do
21
+ filter_fields(col.attributes)
22
+ end
23
+ end
24
+ end.join.html_safe
25
+ end
26
+
3
27
  def filter_for(method, klass, options={})
4
- options ||= {}
5
- options = options.dup
6
28
  options[:as] ||= default_filter_type(klass, method)
7
- expand_block_options!(options)
8
29
  return "" unless options[:as]
9
30
  field_type = options.delete(:as)
10
31
  wrapper_options = options[:wrapper_html] || {}
@@ -15,22 +36,26 @@ module SimpleAdmin
15
36
  end
16
37
 
17
38
  def filter_string_input(klass, method, options = {})
18
- field_name = "#{method}_contains"
39
+ field_name = "#{method}_cont"
40
+ label_content = options[:title] || options[:label] || "Search #{method.to_s.titlecase}" unless options[:label] == false
19
41
 
20
- [ label(field_name, options[:title] || "Search #{method.to_s.titlecase}"),
21
- text_field_tag(field_name, params[field_name] || '')
22
- ].join("\n").html_safe
42
+ content = []
43
+ content << label(field_name, label_content) unless label_content.blank?
44
+ content << text_field_tag(field_name, params[field_name] || '', options[:input_html] || {})
45
+ content.join("\n").html_safe
23
46
  end
24
47
 
25
48
  def filter_date_range_input(klass, method, options = {})
26
- gt_field_name = "#{method}_gte"
27
- lt_field_name = "#{method}_lte"
28
-
29
- [ label(gt_field_name, options[:title] || method.to_s.titlecase),
30
- filter_date_text_field(klass, gt_field_name),
31
- " - ",
32
- filter_date_text_field(klass, lt_field_name)
33
- ].join("\n").html_safe
49
+ gt_field_name = "#{method}_gteq"
50
+ lt_field_name = "#{method}_lteq"
51
+ label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
52
+
53
+ content = []
54
+ content << label(gt_field_name, label_content) unless label_content.blank?
55
+ content << filter_date_text_field(klass, gt_field_name)
56
+ content << " - "
57
+ content << filter_date_text_field(klass, lt_field_name)
58
+ content.join("\n").html_safe
34
59
  end
35
60
 
36
61
  def filter_date_text_field(klass, method)
@@ -39,12 +64,18 @@ module SimpleAdmin
39
64
  end
40
65
 
41
66
  def filter_numeric_input(klass, method, options = {})
67
+ field_name = "#{method}_numeric"
42
68
  filters = numeric_filters_for_method(method, options.delete(:filters) || default_numeric_filters)
43
69
  current_filter = current_numeric_scope(klass, filters)
44
- filter_select = select_tag '', options_for_select(filters, current_filter), :onchange => "document.getElementById('#{method}_numeric').name = '' + this.value + '';"
45
- filter_input = text_field_tag(current_filter, params[current_filter] || '', :size => 10, :id => "#{method}_numeric")
46
-
47
- [ label_tag(method, options[:title]), filter_select, " ", filter_input].join("\n").html_safe
70
+ label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
71
+
72
+ content = []
73
+ content << label(field_name, label_content) unless label_content.blank?
74
+ content << select_tag('', options_for_select(filters, current_filter),
75
+ {:onchange => "document.getElementById('#{method}_numeric').name = '' + this.value + '';"}.merge(options[:input_html] || {}))
76
+ content << " "
77
+ content << text_field_tag(current_filter, params[current_filter] || '', {:size => 10, :id => field_name}.merge(options[:input_html] || {}))
78
+ content.join("\n").html_safe
48
79
  end
49
80
 
50
81
  def numeric_filters_for_method(method, filters)
@@ -63,7 +94,7 @@ module SimpleAdmin
63
94
 
64
95
  def filter_select_input(klass, method, options = {})
65
96
  association_name = method.to_s.gsub(/_id$/, '').to_sym
66
- input_name = if reflection = reflection_for(klass, association_name)
97
+ field_name = if reflection = reflection_for(klass, association_name)
67
98
  if [:has_and_belongs_to_many, :has_many].include?(reflection.macro)
68
99
  "#{association_name.to_s.singularize}_ids"
69
100
  else
@@ -72,11 +103,15 @@ module SimpleAdmin
72
103
  else
73
104
  association_name
74
105
  end
75
- input_name = (input_name.to_s + "_eq").to_sym
106
+ field_name = (field_name.to_s + "_eq")
76
107
  collection = find_collection_for_column(klass, association_name, options)
77
- [ label(input_name, options[:title] || method.to_s.titlecase),
78
- select_tag(input_name, options_for_select(collection, params[input_name]), :include_blank => options[:include_blank] || 'Any')
79
- ].join("\n").html_safe
108
+ label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
109
+
110
+ content = []
111
+ content << label(field_name, label_content) unless label_content.blank?
112
+ content << select_tag(field_name, options_for_select(collection, params[field_name.to_sym]),
113
+ {:include_blank => options[:include_blank] || 'Any'}.merge(options[:input_html] || {}))
114
+ content.join("\n").html_safe
80
115
  end
81
116
 
82
117
  def generate_association_input_name(klass, method) #:nodoc:
@@ -110,26 +145,40 @@ module SimpleAdmin
110
145
  end
111
146
 
112
147
  def boolean_collection(klass, column, options)
113
- [['Yes', true], ['No', false]]
148
+ [['No', false], ['Yes', true]]
149
+ end
150
+
151
+ def filter_boolean_input(klass, method, options = {})
152
+ field_name = options[:name] || (generate_association_input_name(klass, method).to_s + "_in")
153
+ label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
154
+
155
+ content = []
156
+ content << check_box_tag(field_name, true, params[field_name.to_sym], options)
157
+ content << label(field_name, label_content) unless label_content.blank?
158
+ content.join("\n").html_safe
114
159
  end
115
160
 
116
161
  def filter_check_boxes_input(klass, method, options = {})
117
- input_name = (generate_association_input_name(klass, method).to_s + "_in").to_sym
162
+ field_name = options[:name] || (generate_association_input_name(klass, method).to_s + "_in")
118
163
  collection = find_collection_for_column(klass, method, options)
119
- selected_values = params[input_name] || []
164
+ selected_values = params[field_name.to_sym] || []
120
165
  checkboxes = content_tag :div, :class => "check_boxes_wrapper" do
121
166
  collection.map do |c|
122
167
  label = c.is_a?(Array) ? c.first : c
123
168
  value = c.is_a?(Array) ? c.last : c
124
- "<label><input type=\"checkbox\" name=\"#{input_name}[]\" value=\"#{value}\" #{selected_values.include?(value) ? "checked" : ""}/> #{label}</label>"
169
+ "<label><input type=\"checkbox\" name=\"#{field_name}[]\" value=\"#{value}\" #{selected_values.include?(value) ? "checked" : ""}/> #{label}</label>"
125
170
  end.join("\n").html_safe
126
171
  end
172
+ label_content = options[:title] || options[:label] || "#{method.to_s.titlecase}" unless options[:label] == false
127
173
 
128
- [ label(input_name, options[:title] || method.to_s.titlecase),
129
- checkboxes
130
- ].join("\n").html_safe
174
+ content = []
175
+ content << label(field_name, label_content) unless label_content.blank?
176
+ content << checkboxes
177
+ content.join("\n").html_safe
131
178
  end
132
179
 
180
+ alias_method :filter_checkboxes_input, :filter_check_boxes_input
181
+
133
182
  # Returns the default filter type for a given attribute
134
183
  def default_filter_type(klass, method)
135
184
  if column = column_for(klass, method)
@@ -143,6 +192,8 @@ module SimpleAdmin
143
192
  return :numeric
144
193
  when :float, :decimal
145
194
  return :numeric
195
+ when :boolean
196
+ return :boolean
146
197
  end
147
198
  end
148
199
 
@@ -9,7 +9,7 @@ module SimpleAdmin
9
9
  when :attribute
10
10
  form.input col.attribute, options
11
11
  when :content
12
- instance_exec(@resource, &col.data)
12
+ instance_exec(@resource, form, col, &col.data)
13
13
  when :fieldset
14
14
  content_tag :fieldset, options do
15
15
  content_tag :legend do
@@ -24,5 +24,11 @@ module SimpleAdmin
24
24
  end
25
25
  end.join.html_safe
26
26
  end
27
+
28
+ def form_field(form, col)
29
+ options = (col.options || {}).dup
30
+ expand_block_options!(options)
31
+ form.input col.attribute, options
32
+ end
27
33
  end
28
34
  end
@@ -35,12 +35,12 @@ module SimpleAdmin
35
35
  content = ""
36
36
  # If we are currently showing, then check for edit and destroy action items
37
37
  if params[:action].to_sym == :show
38
- if controller.action_methods.include?('edit')
38
+ if @interface.actions.include?(:edit)
39
39
  content << link_to("Edit #{@interface.member.titlecase}",
40
40
  send("edit_simple_admin_#{@interface.member}_path", @object))
41
41
  end
42
42
  content << "&nbsp;"
43
- if controller.action_methods.include?("destroy")
43
+ if @interface.actions.include?(:destroy)
44
44
  content << link_to("Delete #{@interface.member.titlecase}",
45
45
  send("simple_admin_#{@interface.member}_path", @object),
46
46
  :method => :delete, :confirm => "Are you sure you want to delete this?")
@@ -48,9 +48,11 @@ module SimpleAdmin
48
48
  end
49
49
  # If we are not showing an item or creating a new one, then check for new action items
50
50
  unless [:new, :show, :dashboard].include?(params[:action].to_sym)
51
- if controller.action_methods.include?('new')
52
- content << link_to("New #{@interface.member.titlecase}",
53
- send("new_simple_admin_#{@interface.member}_path"))
51
+ if @interface.constant && @interface.member
52
+ if @interface.actions.include?(:new)
53
+ content << link_to("New #{@interface.member.titlecase}",
54
+ send("new_simple_admin_#{@interface.member}_path"))
55
+ end
54
56
  end
55
57
  end
56
58
  content.html_safe
@@ -1,10 +1,10 @@
1
1
  module SimpleAdmin
2
2
  module PathHelper
3
- def resource_path(res)
3
+ def resource_path(res, options={})
4
4
  if res.new_record?
5
- send("simple_admin_#{@interface.collection}_path")
5
+ send("simple_admin_#{@interface.collection}_path", options)
6
6
  else
7
- send("simple_admin_#{@interface.member}_path", res)
7
+ send("simple_admin_#{@interface.member}_path", res, options)
8
8
  end
9
9
  end
10
10
  end
@@ -30,10 +30,11 @@ module SimpleAdmin
30
30
  end
31
31
 
32
32
  def resource_actions(object)
33
- links = link_to "View", send("simple_admin_#{@interface.member}_path", object), :class => "member_link view_link"
34
- links += link_to "Edit", send("edit_simple_admin_#{@interface.member}_path", object), :class => "member_link edit_link"
35
- links += link_to "Delete", send("simple_admin_#{@interface.member}_path", object), :method => :delete, :confirm => "Are you sure you want to delete this?", :class => "member_link delete_link"
36
- links
33
+ links = ""
34
+ links += link_to "View", send("simple_admin_#{@interface.member}_path", object), :class => "member_link view_link" if @interface.actions.include?(:show)
35
+ links += link_to "Edit", send("edit_simple_admin_#{@interface.member}_path", object), :class => "member_link edit_link" if @interface.actions.include?(:edit)
36
+ links += link_to "Delete", send("simple_admin_#{@interface.member}_path", object), :method => :delete, :confirm => "Are you sure you want to delete this?", :class => "member_link delete_link" if @interface.actions.include?(:destroy)
37
+ raw links
37
38
  end
38
39
  end
39
40
  end
@@ -24,6 +24,9 @@
24
24
  <%= yield :content %>
25
25
  </div>
26
26
  </div>
27
+ <div id="filters">
28
+ <%= yield :filters %>
29
+ </div>
27
30
  <div id="sidebar">
28
31
  <%= yield :sidebar %>
29
32
  </div>
@@ -1,82 +1,117 @@
1
1
  <% content_for :content do %>
2
- <div class="paginated_collection">
3
- <div class="pagination_information">
4
- <% if @collection.num_pages < 2 %>
5
- <% if @collection.size == 0 %>
6
- No <%= @interface.collection.titleize %> found
2
+ <% if @collection %>
3
+ <div class="paginated_collection <%= @interface.collection.to_s %>">
4
+ <div class="pagination_information">
5
+ <% if @collection.num_pages < 2 %>
6
+ <% if @collection.size == 0 %>
7
+ No <%= @interface.collection.titleize %> found
8
+ <% elsif @collection.size == 1 %>
9
+ Displaying <b>1</b> <%= @interface.member.titleize %>
10
+ <% else %>
11
+ Displaying <b>all <%= @collection.size %></b> <%= @interface.collection.titleize %>
12
+ <% end %>
7
13
  <% elsif @collection.size == 1 %>
8
- Displaying <b>1</b> <%= @interface.member.titleize %>
14
+ Displaying <%= @interface.member.titleize %>: <%= pretty_format(@collection.first) %>;
15
+ <b><%= @collection.total_count %></b> in total
9
16
  <% else %>
10
- Displaying <b>all <%= @collection.size %></b> <%= @interface.collection.titleize %>
17
+ Displaying <%= @interface.collection.titleize %>
18
+ <b><%= (@collection.current_page * (params[:per_page] || SimpleAdmin.default_per_page).to_i) - (params[:per_page] || SimpleAdmin.default_per_page).to_i + 1 %>&nbsp;-&nbsp;<%= (@collection.current_page * (params[:per_page] || SimpleAdmin.default_per_page).to_i) > @collection.total_count ? @collection.total_count : (@collection.current_page * (params[:per_page] || SimpleAdmin.default_per_page).to_i) %></b>
19
+ of <b><%= @collection.total_count %></b> in total
11
20
  <% end %>
12
- <% else %>
13
- Displaying <%= @interface.collection.titleize %>
14
- <b><%= (@collection.current_page * SimpleAdmin.default_per_page) - SimpleAdmin.default_per_page + 1 %>&nbsp;-&nbsp;<%= (@collection.current_page * SimpleAdmin.default_per_page) > @collection.total_count ? @collection.total_count : (@collection.current_page * SimpleAdmin.default_per_page) %></b>
15
- of <b><%= @collection.total_count %></b> in total
16
- <% end %>
17
- </div>
18
- <div class="paginated_collection_contents">
19
- <div class="index_content">
20
- <div class="index_as_table">
21
- <table border="0" class="index_table" cellspacing="0" cellpadding="0" id="<%= @interface.collection %>">
22
- <thead>
23
- <tr>
24
- <% @interface.attributes_for(:index).each do |col| %>
25
- <th class="<%= sortable_header_classes_for(col) %>">
26
- <%= link_to(col.title,
27
- request.query_parameters.merge(:order => "#{col.sort_key}_#{order_for_sort_key(col.sort_key)}").except(:page)) %>
28
- </th>
21
+ <% editable = @interface.attributes_for(:index).any? {|a| a.editable} %>
22
+ <% if editable %>
23
+ | <%= link_to((params[:editing] ? 'Stop Editing' : 'Edit'), request.query_parameters.merge(:editing => (params[:editing] ? nil : 1))) %>
24
+ | <%= link_to((params[:mode] == 'approvals' ? 'Rows' : 'Approvals'), request.query_parameters.merge(
25
+ :mode => (params[:mode] == 'approvals' ? nil : 'approvals'),
26
+ :per_page => (params[:mode] == 'approvals' ? nil : 1))) %>
27
+ <% end %>
28
+ </div>
29
+ <div class="paginated_collection_contents <%= params[:mode] || 'rows' %> <%= 'editing' if params[:editing] %>">
30
+ <div class="index_content">
31
+ <% if params[:mode] == 'approvals' %>
32
+ <div class="index_as_pages">
33
+ <%= paginate(@collection) %>
34
+ <% if @resource = @collection.first %>
35
+ <%= send(SimpleAdmin.form_for_helper, @resource, :as => @interface.member.to_sym,
36
+ :url => resource_path(@resource), :id => "#{@interface.member}_#{@resource.id}", :html => {:onsubmit => "return false", :class => "inline simple_form formtastic #{@interface.member}"}) do |form| %>
37
+ <%= form_fields(form, @interface.attributes_for(:index, 'approvals')) %>
29
38
  <% end %>
30
- <th>&nbsp;</th>
31
- </tr>
32
- </thead>
33
- <tbody>
34
- <% @collection.each do |object| %>
35
- <tr class="<%= cycle('odd', 'even') %>">
39
+ <% end %>
40
+ </div>
41
+ <% else %>
42
+ <div class="index_as_table">
43
+ <table border="0" class="index_table" cellspacing="0" cellpadding="0" id="<%= @interface.collection %>">
44
+ <thead>
45
+ <tr>
36
46
  <% @interface.attributes_for(:index).each do |col| %>
37
- <td>
38
- <% if col.data %>
39
- <%= instance_exec(object, &col.data) %>
40
- <% else %>
41
- <%= pretty_format(object.send(col.attribute)) %>
42
- <% end %>
43
- </td>
47
+ <th class="<%= sortable_header_classes_for(col) %> <%= col.attribute %>">
48
+ <%= link_to(col.title,
49
+ request.query_parameters.merge(:order => "#{col.sort_key}_#{order_for_sort_key(col.sort_key)}").except(:page)) %>
50
+ </th>
44
51
  <% end %>
45
- <td><%= resource_actions(object) %></td>
52
+ <th>&nbsp;</th>
46
53
  </tr>
47
- <% end %>
48
- </tbody>
49
- </table>
54
+ </thead>
55
+ <tbody>
56
+ <% @collection.each do |object| %>
57
+ <tr class="<%= cycle('odd', 'even') %>">
58
+ <% @interface.attributes_for(:index).each do |col| %>
59
+ <td class="<%= col.attribute %>">
60
+ <% if col.editable && params[:editing] %>
61
+ <%= send(SimpleAdmin.form_for_helper, object, :as => @interface.member.to_sym,
62
+ :url => resource_path(object), :id => "#{@interface.member}_#{object.id}", :html => {:class => "inline simple_form formtastic #{@interface.member}"}) do |form| %>
63
+ <%= form_field(form, col) %>
64
+ <% end %>
65
+ <% elsif col.data %>
66
+ <%= instance_exec(object, &col.data) %>
67
+ <% else %>
68
+ <%= pretty_format(object.send(col.attribute)) %>
69
+ <% end %>
70
+ </td>
71
+ <% end %>
72
+ <td><%= resource_actions(object) %></td>
73
+ </tr>
74
+ <% end %>
75
+ </tbody>
76
+ </table>
77
+ </div>
78
+ <% end %>
50
79
  </div>
51
80
  </div>
81
+ <% if params[:mode] != 'approvals' %>
82
+ <div id="index_footer">
83
+ Download:&nbsp;
84
+ <% [:csv, :xml, :json].each do |format| %>
85
+ <%= link_to format.to_s.upcase, { :format => format}.merge(request.query_parameters.except(:commit, :format)) %>
86
+ <% end %>
87
+ </div>
88
+ <%= paginate(@collection) %>
89
+ <% end %>
90
+
52
91
  </div>
53
- <div id="index_footer">
54
- Download:&nbsp;
55
- <% [:csv, :xml, :json].each do |format| %>
56
- <%= link_to format.to_s.upcase, { :format => format}.merge(request.query_parameters.except(:commit, :format)) %>
57
- <% end %>
58
- </div>
59
- <%= paginate(@collection) %>
60
- </div>
92
+ <% end %>
61
93
  <% end %>
62
94
 
63
- <% content_for :sidebar do %>
64
- <div class="panel sidebar_section" id="filters_sidebar_section">
65
- <h3>Filters</h3>
66
- <div class="panel_contents">
67
- <%= form_tag '', :class => "q_search", :id => "q_search", :method => "get" do %>
68
- <% @interface.filters_for(:index).each do |col| %>
69
- <%= filter_for(col.attribute, @interface.constant, col.options) %>
95
+ <% content_for :filters do %>
96
+ <% filters = @interface.filters_for(:index) %>
97
+ <% unless filters.blank? %>
98
+ <div class="panel sidebar_section" id="filters_sidebar_section">
99
+ <h3>Filters</h3>
100
+ <div class="panel_contents">
101
+ <%= form_tag '', :class => "q_search", :id => "q_search", :method => "get" do %>
102
+ <%= filter_fields(filters) %>
103
+ <div class="buttons">
104
+ <input name="commit" type="submit" value="Filter" />
105
+ <a href="?" class="clear_filters_btn">Clear Filters</a>
106
+ <input id="order" name="order" type="hidden" value="id_desc" />
107
+ <input id="scope" name="scope" type="hidden" />
108
+ </div>
70
109
  <% end %>
71
- <div class="buttons">
72
- <input name="commit" type="submit" value="Filter" />
73
- <a href="?" class="clear_filters_btn">Clear Filters</a>
74
- <input id="order" name="order" type="hidden" value="id_desc" />
75
- <input id="scope" name="scope" type="hidden" />
76
- </div>
77
- <% end %>
110
+ </div>
78
111
  </div>
79
- </div>
112
+ <% end %>
113
+ <% end %>
80
114
 
115
+ <% content_for :sidebar do %>
81
116
  <%= sidebars %>
82
117
  <% end %>
@@ -1,7 +1,7 @@
1
1
  SimpleAdmin::Engine.routes.draw do
2
2
  root :to => "admin#dashboard"
3
3
  SimpleAdmin::registered.each do |interface|
4
- resources interface.collection,
4
+ resources interface.route,
5
5
  :controller => :admin,
6
6
  :as => "simple_admin_#{interface.collection}",
7
7
  :defaults => {:interface => interface.collection}
@@ -111,4 +111,12 @@ module SimpleAdmin
111
111
  @message = "SimpleAdmin interface unknown, make sure you mount SimpleAdmin in your routes and that you have registered an interface for this resource"
112
112
  end
113
113
  end
114
+
115
+ class ActionNotAllowed < StandardError
116
+ attr_accessor :message
117
+
118
+ def initialize(action)
119
+ @message = "SimpleAdmin #{action} action undefined, make sure you have allowed this action in your interface definition."
120
+ end
121
+ end
114
122
  end
@@ -33,7 +33,7 @@ module SimpleAdmin
33
33
  # :sort_key a column name used when sorting this column (defaults to the column for this attribute)
34
34
  #
35
35
  def attribute(name, options={}, &block)
36
- attr = @attributes.find {|attr| attr.attribute == name.to_sym }
36
+ attr = @attributes.find {|attr| attr.attribute == name.to_sym && attr.mode == options[:mode]}
37
37
  unless attr
38
38
  attr = OpenStruct.new
39
39
  @attributes << attr
@@ -44,6 +44,8 @@ module SimpleAdmin
44
44
  attr.data = block
45
45
  attr.title = options[:title] || name.to_s.titleize
46
46
  attr.sortable = options[:sortable].nil? || !(options[:sortable] === false)
47
+ attr.editable = options[:editable] === true
48
+ attr.mode = options[:mode]
47
49
  attr.sort_key = (options[:sort_key] || name).to_s
48
50
  attr
49
51
  end
@@ -58,6 +60,7 @@ module SimpleAdmin
58
60
  cont.kind = :content
59
61
  cont.options = options
60
62
  cont.data = block
63
+ cont.mode = options[:mode]
61
64
  cont
62
65
  end
63
66
 
@@ -81,6 +84,7 @@ module SimpleAdmin
81
84
  @attributes << sect
82
85
  sect.kind = options.delete(:kind) || :section
83
86
  sect.options = options
87
+ sect.mode = options[:mode]
84
88
  sect.attributes = []
85
89
  save_attributes = @attributes
86
90
  @attributes = sect.attributes
@@ -100,21 +104,23 @@ module SimpleAdmin
100
104
  # :except An array of excluded attribute names as symbols
101
105
  # :only An array of attribute names for this view
102
106
  #
103
- def defaults(options={})
107
+ def defaults(options={})
104
108
  clear
105
109
  if @section.section == :form
106
- association_columns = @interface.constant.reflections.map do |name, ref|
110
+ reflections = @interface.constant.reflections rescue []
111
+ association_columns = reflections.map do |name, ref|
107
112
  if ref.macro == :belongs_to && ref.options[:polymorphic] != true
108
113
  name.to_sym
109
114
  end
110
115
  end.compact
111
116
 
112
- cols = @interface.constant.content_columns.map {|col| col.name.to_sym }
117
+ content_columns = @interface.constant.content_columns rescue []
118
+ cols = content_columns.map {|col| col.name.to_sym }
113
119
  cols += association_columns
114
120
  cols -= SKIPPED_COLUMNS
115
121
  cols.compact!
116
122
  else
117
- cols = @interface.constant.columns.map{|col| col.name.to_sym }
123
+ cols = @interface.constant.columns.map{|col| col.name.to_sym } rescue []
118
124
  end
119
125
  cols -= options[:except] if options[:except]
120
126
  cols &= options[:only] if options[:only]
@@ -6,7 +6,7 @@ module SimpleAdmin
6
6
  isolate_namespace SimpleAdmin
7
7
  initializer 'simple_admin' do |app|
8
8
  if Rails.env == "development"
9
- simple_admin_reloader = ActiveSupport::FileUpdateChecker.new(Dir["app/admin/**/*"], true) do
9
+ simple_admin_reloader = ActiveSupport::FileUpdateChecker.new(Dir["app/admin/**/*"], {}) do
10
10
  SimpleAdmin.unregister
11
11
  Rails.application.reload_routes!
12
12
  end
@@ -9,24 +9,28 @@ module SimpleAdmin
9
9
  @constant = resource
10
10
  @symbol = resource.name.underscore.to_sym
11
11
  else
12
- @constant = resource.to_s.camelize.singularize.constantize
12
+ @constant = resource.to_s.camelize.singularize.constantize rescue nil
13
13
  @symbol = resource.to_sym
14
14
  end
15
15
  @member = @symbol.to_s.singularize
16
16
  @collection = @symbol.to_s.pluralize
17
17
  @options = options
18
18
  @sections = {}
19
+ @block = block
19
20
  @before = []
20
- @builder = SimpleAdmin::Builder.new(self, &block)
21
21
  self
22
22
  end
23
23
 
24
+ def route
25
+ @options[:route] || @collection
26
+ end
27
+
24
28
  def filters_for(sym)
25
29
  options_for(sym)[:filters].attributes
26
30
  end
27
31
 
28
- def attributes_for(sym)
29
- options_for(sym)[:attributes].attributes
32
+ def attributes_for(sym, mode=nil)
33
+ options_for(sym)[:attributes].attributes.select{|a| a.mode == mode}
30
34
  end
31
35
 
32
36
  def sidebars_for(sym)
@@ -48,6 +52,7 @@ module SimpleAdmin
48
52
  private
49
53
 
50
54
  def section(sym)
55
+ @builder ||= SimpleAdmin::Builder.new(self, &@block)
51
56
  @sections[sym] ||= SimpleAdmin::Section.new(self, sym)
52
57
  @sections[sym]
53
58
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleAdmin
2
- VERSION = "0.5.2"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -20,9 +20,8 @@ Gem::Specification.new do |s|
20
20
  s.require_paths = ["lib"]
21
21
 
22
22
  # Formtastic is a dependency if you are using the default form helper
23
- s.add_runtime_dependency("rails", [">= 3.1.0rc4"])
23
+ s.add_runtime_dependency("rails", [">= 3.1.0"])
24
24
  s.add_runtime_dependency("kaminari", [">= 0"])
25
- s.add_runtime_dependency("meta_search", [">= 1.1.0.pre"])
26
25
 
27
26
  s.add_development_dependency("rspec", [">= 2.6.0"])
28
27
  s.add_development_dependency("rspec-rails", [">= 2.6.0"])
@@ -92,4 +92,13 @@ describe SimpleAdmin::AdminController do
92
92
  end
93
93
  end
94
94
 
95
+ context "action limits" do
96
+ it "should prevent destroy from firing" do
97
+ SimpleAdmin::Interface.any_instance.expects(:actions).returns([:index, :show, :edit])
98
+ expect {
99
+ delete :destroy, :id => @thing.id, :interface => "things"
100
+ }.to raise_error(SimpleAdmin::ActionNotAllowed)
101
+ end
102
+ end
103
+
95
104
  end
@@ -44,11 +44,19 @@ describe SimpleAdmin::Interface do
44
44
  @interface.should_not be_nil
45
45
  end
46
46
 
47
- it "executes a block using a builder if submitted" do
47
+ it "doesn't execute a block using a builder when initialized" do
48
+ SimpleAdmin::Builder.any_instance.expects(:success).never
49
+ @interface = SimpleAdmin.register(:things) do
50
+ success
51
+ end
52
+ end
53
+
54
+ it "executes a block using a builder if a section is requested" do
48
55
  SimpleAdmin::Builder.any_instance.expects(:success)
49
56
  @interface = SimpleAdmin.register(:things) do
50
57
  success
51
58
  end
59
+ @interface.send(:section, :custom)
52
60
  end
53
61
 
54
62
  it "retrieves the attributes for a section" do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_admin
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 5
9
- - 2
10
- version: 0.5.2
8
+ - 6
9
+ - 0
10
+ version: 0.6.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jeff Rafter
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-17 00:00:00 -07:00
18
+ date: 2012-03-16 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -26,14 +26,12 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- hash: 15424109
29
+ hash: 3
30
30
  segments:
31
31
  - 3
32
32
  - 1
33
33
  - 0
34
- - rc
35
- - 4
36
- version: 3.1.0rc4
34
+ version: 3.1.0
37
35
  type: :runtime
38
36
  version_requirements: *id001
39
37
  - !ruby/object:Gem::Dependency
@@ -50,27 +48,10 @@ dependencies:
50
48
  version: "0"
51
49
  type: :runtime
52
50
  version_requirements: *id002
53
- - !ruby/object:Gem::Dependency
54
- name: meta_search
55
- prerelease: false
56
- requirement: &id003 !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- hash: 961915996
62
- segments:
63
- - 1
64
- - 1
65
- - 0
66
- - pre
67
- version: 1.1.0.pre
68
- type: :runtime
69
- version_requirements: *id003
70
51
  - !ruby/object:Gem::Dependency
71
52
  name: rspec
72
53
  prerelease: false
73
- requirement: &id004 !ruby/object:Gem::Requirement
54
+ requirement: &id003 !ruby/object:Gem::Requirement
74
55
  none: false
75
56
  requirements:
76
57
  - - ">="
@@ -82,11 +63,11 @@ dependencies:
82
63
  - 0
83
64
  version: 2.6.0
84
65
  type: :development
85
- version_requirements: *id004
66
+ version_requirements: *id003
86
67
  - !ruby/object:Gem::Dependency
87
68
  name: rspec-rails
88
69
  prerelease: false
89
- requirement: &id005 !ruby/object:Gem::Requirement
70
+ requirement: &id004 !ruby/object:Gem::Requirement
90
71
  none: false
91
72
  requirements:
92
73
  - - ">="
@@ -98,11 +79,11 @@ dependencies:
98
79
  - 0
99
80
  version: 2.6.0
100
81
  type: :development
101
- version_requirements: *id005
82
+ version_requirements: *id004
102
83
  - !ruby/object:Gem::Dependency
103
84
  name: shoulda
104
85
  prerelease: false
105
- requirement: &id006 !ruby/object:Gem::Requirement
86
+ requirement: &id005 !ruby/object:Gem::Requirement
106
87
  none: false
107
88
  requirements:
108
89
  - - ">="
@@ -112,11 +93,11 @@ dependencies:
112
93
  - 0
113
94
  version: "0"
114
95
  type: :development
115
- version_requirements: *id006
96
+ version_requirements: *id005
116
97
  - !ruby/object:Gem::Dependency
117
98
  name: factory_girl
118
99
  prerelease: false
119
- requirement: &id007 !ruby/object:Gem::Requirement
100
+ requirement: &id006 !ruby/object:Gem::Requirement
120
101
  none: false
121
102
  requirements:
122
103
  - - ">="
@@ -126,11 +107,11 @@ dependencies:
126
107
  - 0
127
108
  version: "0"
128
109
  type: :development
129
- version_requirements: *id007
110
+ version_requirements: *id006
130
111
  - !ruby/object:Gem::Dependency
131
112
  name: mocha
132
113
  prerelease: false
133
- requirement: &id008 !ruby/object:Gem::Requirement
114
+ requirement: &id007 !ruby/object:Gem::Requirement
134
115
  none: false
135
116
  requirements:
136
117
  - - ">"
@@ -140,11 +121,11 @@ dependencies:
140
121
  - 0
141
122
  version: "0"
142
123
  type: :development
143
- version_requirements: *id008
124
+ version_requirements: *id007
144
125
  - !ruby/object:Gem::Dependency
145
126
  name: capybara
146
127
  prerelease: false
147
- requirement: &id009 !ruby/object:Gem::Requirement
128
+ requirement: &id008 !ruby/object:Gem::Requirement
148
129
  none: false
149
130
  requirements:
150
131
  - - ">="
@@ -156,11 +137,11 @@ dependencies:
156
137
  - 0
157
138
  version: 0.4.0
158
139
  type: :development
159
- version_requirements: *id009
140
+ version_requirements: *id008
160
141
  - !ruby/object:Gem::Dependency
161
142
  name: sqlite3-ruby
162
143
  prerelease: false
163
- requirement: &id010 !ruby/object:Gem::Requirement
144
+ requirement: &id009 !ruby/object:Gem::Requirement
164
145
  none: false
165
146
  requirements:
166
147
  - - ">="
@@ -170,7 +151,7 @@ dependencies:
170
151
  - 0
171
152
  version: "0"
172
153
  type: :development
173
- version_requirements: *id010
154
+ version_requirements: *id009
174
155
  description: Use SimpleAdmin to build out filtering, searchable, editable interfaces for your models
175
156
  email:
176
157
  - jeffrafter@gmail.com