simple_admin 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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