merb-admin 0.4.3 → 0.4.4

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/README.markdown CHANGED
@@ -8,13 +8,13 @@ It currently offers the features listed [here](http://sferik.tadalist.com/lists/
8
8
 
9
9
  At the command prompt, type:
10
10
 
11
- sudo gem install sferik-merb-admin -s http://gems.github.com
11
+ sudo gem install merb-admin -s http://gemcutter.org
12
12
 
13
13
  ## Install it
14
14
 
15
15
  In your app, add the following dependency to `config/dependencies.rb`:
16
16
 
17
- dependency "sferik-merb-admin", "0.4.3", :require_as => "merb-admin"
17
+ dependency "merb-admin", "0.4.4"
18
18
 
19
19
  Add the following route to `config/router.rb`:
20
20
 
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ AUTHOR = "Erik Michaels-Ober"
9
9
  EMAIL = "sferik@gmail.com"
10
10
  HOMEPAGE = "http://twitter.com/sferik"
11
11
  SUMMARY = "MerbAdmin is a Merb plugin that provides an easy-to-use interface for managing your data."
12
- GEM_VERSION = "0.4.3"
12
+ GEM_VERSION = "0.4.4"
13
13
 
14
14
  spec = Gem::Specification.new do |s|
15
15
  s.rubyforge_project = "merb"
@@ -13,6 +13,8 @@ class MerbAdmin::Main < MerbAdmin::Application
13
13
 
14
14
  def list
15
15
  options = {}
16
+ merge_sort!(options)
17
+ merge_sort_reverse!(options)
16
18
 
17
19
  if params[:all]
18
20
  options = {
@@ -123,6 +125,18 @@ class MerbAdmin::Main < MerbAdmin::Application
123
125
  raise NotFound unless @object
124
126
  end
125
127
 
128
+ def merge_sort!(options)
129
+ return unless params[:sort]
130
+ sort = params[:sort] || "id"
131
+ options.merge!(:sort => sort)
132
+ end
133
+
134
+ def merge_sort_reverse!(options)
135
+ return unless params[:sort_reverse]
136
+ reverse = params[:sort_reverse] == "true"
137
+ options.merge!(:sort_reverse => reverse)
138
+ end
139
+
126
140
  def update_has_one_association(association, id)
127
141
  model = MerbAdmin::AbstractModel.new(association[:child_model])
128
142
  if object = model.get(id)
@@ -3,7 +3,9 @@ module Merb
3
3
  module MerbAdmin
4
4
  module MainHelper
5
5
  def object_title(object)
6
- if object.respond_to?(:name)
6
+ if object.nil?
7
+ nil
8
+ elsif object.respond_to?(:name)
7
9
  object.name
8
10
  elsif object.respond_to?(:title)
9
11
  object.title
@@ -12,6 +14,41 @@ module Merb
12
14
  end
13
15
  end
14
16
 
17
+ def object_property(object, property)
18
+ property_type = property[:type]
19
+ property_name = property[:name]
20
+ case property_type
21
+ when :boolean
22
+ if object.send(property_name) == true
23
+ Builder::XmlMarkup.new.img(:src => image_path("icon-yes.gif"), :alt => "True")
24
+ else
25
+ Builder::XmlMarkup.new.img(:src => image_path("icon-on.gif"), :alt => "False")
26
+ end
27
+ when :date_time
28
+ value = object.send(property_name)
29
+ value.respond_to?(:strftime) ? value.strftime("%b. %d, %Y, %I:%M%p") : nil
30
+ when :date
31
+ value = object.send(property_name)
32
+ value.respond_to?(:strftime) ? value.strftime("%b. %d, %Y") : nil
33
+ when :time
34
+ value = object.send(property_name)
35
+ value.respond_to?(:strftime) ? value.strftime("%I:%M%p") : nil
36
+ when :string
37
+ object.send(property_name).to_s.truncate(50)
38
+ when :text
39
+ object.send(property_name).to_s.truncate(50)
40
+ when :integer
41
+ association = @abstract_model.belongs_to_associations.select{|a| a[:child_key].first == property_name}.first
42
+ if association
43
+ object_title(object.send(association[:name]))
44
+ else
45
+ object.send(property_name)
46
+ end
47
+ else
48
+ object.send(property_name)
49
+ end
50
+ end
51
+
15
52
  # Given a page count and the current page, we generate a set of pagination
16
53
  # links.
17
54
  #
@@ -1,11 +1,11 @@
1
1
  <%
2
- name = property[:name]
2
+ property_name = property[:name]
3
3
  length = property[:length]
4
4
  label = property[:pretty_name]
5
5
  required = !property[:nullable?] || property[:serial?]
6
6
  %>
7
7
  <div>
8
- <%= text_field(name, :maxlength => length, :label => label) %>
8
+ <%= text_field(property_name, :maxlength => length, :label => label) %>
9
9
  <p class="help">
10
10
  <%= required ? "Required." : "Optional." %>
11
11
  </p>
@@ -1,7 +1,7 @@
1
1
  <%
2
- name = property[:name]
2
+ property_name = property[:name]
3
3
  label = property[:pretty_name]
4
4
  %>
5
5
  <div>
6
- <%= check_box(name, :label => label) %>
6
+ <%= check_box(property_name, :label => label) %>
7
7
  </div>
@@ -1,11 +1,11 @@
1
1
  <%
2
- name = property[:name]
3
- value = @object.send(name)
2
+ property_name = property[:name]
3
+ value = @object.send(property_name)
4
4
  label = property[:pretty_name]
5
5
  required = !property[:nullable?]
6
6
  %>
7
7
  <div>
8
- <%= text_field(name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : nil, :label => label) %>
8
+ <%= text_field(property_name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : nil, :label => label) %>
9
9
  <p class="help">
10
10
  <%= required ? "Required." : "Optional." %>
11
11
  </p>
@@ -1,11 +1,11 @@
1
1
  <%
2
- name = property[:name]
3
- value = @object.send(name)
2
+ property_name = property[:name]
3
+ value = @object.send(property_name)
4
4
  label = property[:pretty_name]
5
5
  required = !property[:nullable?]
6
6
  %>
7
7
  <div>
8
- <%= text_field(name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S") : nil, :label => label) %>
8
+ <%= text_field(property_name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S") : nil, :label => label) %>
9
9
  <p class="help">
10
10
  <%= required ? "Required." : "Optional." %>
11
11
  </p>
@@ -1,11 +1,11 @@
1
1
  <%
2
- name = property[:name]
2
+ property_name = property[:name]
3
3
  length = property[:length]
4
4
  label = property[:pretty_name]
5
5
  required = !property[:nullable?] || property[:serial?]
6
6
  %>
7
7
  <div>
8
- <%= text_field(name, :maxlength => length, :label => label) %>
8
+ <%= text_field(property_name, :maxlength => length, :label => label) %>
9
9
  <p class="help">
10
10
  <%= required ? "Required." : "Optional." %>
11
11
  </p>
@@ -1,15 +1,15 @@
1
1
  <%
2
- name = association[:name]
2
+ association_name = association[:name]
3
3
  collection = MerbAdmin::AbstractModel.new(association[:child_model]).all.map{|o| [o.id, object_title(o)]}.sort_by{|o| o[1]}
4
- selected = @object.send(name)
4
+ selected = @object.send(association_name)
5
5
  label = association[:pretty_name]
6
6
  %>
7
7
  <fieldset class="module aligned">
8
8
  <h2><%= label %></h2>
9
9
  <div class="form-row">
10
10
  <div>
11
- <%= select(:name => "associations[#{name}][]", :id => name, :collection => collection, :selected => selected.map{|o| o.id.to_s}, :label => label, :multiple => true) %>
12
- <script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("<%= name %>", "<%= name %>", 0, "<%= image_path %>"); });</script>
11
+ <%= select(:name => "associations[#{association_name}][]", :id => association_name, :collection => collection, :selected => selected.map{|o| o.id.to_s}, :label => label, :multiple => true) %>
12
+ <script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("<%= association_name %>", "<%= association_name %>", 0, "<%= image_path %>"); });</script>
13
13
  <p class="help">Hold down "Control", or "Command" on a Mac, to select more than one.</p>
14
14
  </div>
15
15
  </div>
@@ -1,8 +1,8 @@
1
1
  <%
2
2
  child_key = association[:child_key].first
3
- name = association[:name]
3
+ association_name = association[:name]
4
4
  collection = MerbAdmin::AbstractModel.new(association[:child_model]).all.map{|o| [o.id, object_title(o)]}.sort_by{|o| o[1]}
5
- selected = @object.send(association[:name])
5
+ selected = @object.send(association_name)
6
6
  label = association[:pretty_name]
7
7
  required = false
8
8
  @properties.each do |property|
@@ -21,7 +21,7 @@
21
21
  </ul>
22
22
  <% end %>
23
23
  <div>
24
- <%= select(:name => "associations[#{name}][]", :id => name, :collection => collection, :include_blank => true, :selected => selected ? selected.id.to_s : nil, :label => label) %>
24
+ <%= select(:name => "associations[#{association_name}][]", :id => association_name, :collection => collection, :include_blank => true, :selected => selected ? selected.id.to_s : nil, :label => label) %>
25
25
  <p class="help">
26
26
  <%= required ? "Required." : "Optional." %>
27
27
  </p>
@@ -1,16 +1,16 @@
1
1
  <%
2
2
  flag_map = property[:flag_map]
3
- name = property[:name]
3
+ property_name = property[:name]
4
4
  label = property[:pretty_name]
5
5
  required = !property[:nullable?] || property[:serial?]
6
6
  %>
7
7
  <div>
8
8
  <% if flag_map # Enum or Flag type %>
9
9
  <% collection = flag_map.map{|x| [x[1], x[1].to_s.capitalize.gsub('_', ' ')]}.sort{|a, b| a[1] <=> b[1]} %>
10
- <%= select(name, :collection => collection, :label => label) %>
10
+ <%= select(property_name, :collection => collection, :label => label) %>
11
11
  <% else %>
12
12
  <% length = property[:length] %>
13
- <%= text_field(name, :maxlength => length, :label => label) %>
13
+ <%= text_field(property_name, :maxlength => length, :label => label) %>
14
14
  <p class="help">
15
15
  <%= required ? "Required." : "Optional." %>
16
16
  </p>
@@ -1,18 +1,19 @@
1
1
  <fieldset class="module aligned">
2
2
  <% belongs_to_keys = @abstract_model.belongs_to_associations.map{|b| b[:child_key].first} %>
3
3
  <% properties.each do |property| %>
4
- <% name = property[:name] %>
5
- <% next if [:id, :created_at, :created_on, :deleted_at, :updated_at, :updated_on, :deleted_on].include?(name) %>
6
- <% next if belongs_to_keys.include?(name) %>
7
- <div class="<%= @object.errors[name] ? "form-row errors" : "form-row" %>">
8
- <% if @object.errors[name] %>
4
+ <% property_name = property[:name] %>
5
+ <% property_type = property[:type] %>
6
+ <% next if [:id, :created_at, :created_on, :deleted_at, :updated_at, :updated_on, :deleted_on].include?(property_name) %>
7
+ <% next if belongs_to_keys.include?(property_name) %>
8
+ <div class="<%= @object.errors[property_name] ? "form-row errors" : "form-row" %>">
9
+ <% if @object.errors[property_name] %>
9
10
  <ul class="errorlist">
10
- <% @object.errors[name].each do |error| %>
11
+ <% @object.errors[property_name].each do |error| %>
11
12
  <li><%= error %></li>
12
13
  <% end %>
13
14
  </ul>
14
15
  <% end %>
15
- <%= partial(property[:type].to_s, :property => property) -%>
16
+ <%= partial(property_type.to_s, :property => property) -%>
16
17
  </div>
17
18
  <% end %>
18
19
  </fieldset>
@@ -1,11 +1,11 @@
1
1
  <%
2
- name = property[:name]
2
+ property_name = property[:name]
3
3
  length = property[:length]
4
4
  label = property[:pretty_name]
5
5
  required = !property[:nullable?]
6
6
  %>
7
7
  <div>
8
- <%= text_field(name, :size => [50, length].min, :maxlength => length, :label => label) %>
8
+ <%= text_field(property_name, :size => [50, length].min, :maxlength => length, :label => label) %>
9
9
  <p class="help">
10
10
  <%= required ? "Required." : "Optional." %> <%= length %> <%= length == 1 ? "character." : "characters or fewer." %>
11
11
  </p>
@@ -1,11 +1,11 @@
1
1
  <%
2
- name = property[:name]
3
- value = @object.send(name)
2
+ property_name = property[:name]
3
+ value = @object.send(property_name)
4
4
  label = property[:pretty_name]
5
5
  required = !property[:nullable?]
6
6
  %>
7
7
  <div>
8
- <%= text_field(name, :class => "vTimeField", :value => value.respond_to?(:strftime) ? value.strftime("%H:%M:%S") : nil, :label => label) %>
8
+ <%= text_field(property_name, :class => "vTimeField", :value => value.respond_to?(:strftime) ? value.strftime("%H:%M:%S") : nil, :label => label) %>
9
9
  <p class="help">
10
10
  <%= required ? "Required." : "Optional." %>
11
11
  </p>
@@ -1,11 +1,11 @@
1
1
  <%
2
- name = property[:name]
3
- value = @object.send(name)
2
+ property_name = property[:name]
3
+ value = @object.send(property_name)
4
4
  label = property[:pretty_name]
5
5
  required = !property[:nullable?]
6
6
  %>
7
7
  <div>
8
- <%= text_field(name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S") : nil, :label => label) %>
8
+ <%= text_field(property_name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S") : nil, :label => label) %>
9
9
  <p class="help">
10
10
  <%= required ? "Required." : "Optional." %>
11
11
  </p>
@@ -1,5 +1,7 @@
1
1
  <%
2
2
  params = request.params.except(:action, :controller, :model_name)
3
+ sort = params[:sort]
4
+ sort_reverse = params[:sort_reverse]
3
5
  %>
4
6
  <div id="content-main">
5
7
  <ul class="object-tools">
@@ -12,10 +14,10 @@
12
14
  <thead>
13
15
  <tr>
14
16
  <% @properties.each do |property| %>
15
- <% name = property[:name] %>
16
- <% pretty_name = property[:pretty_name] %>
17
- <th>
18
- <%= pretty_name %>
17
+ <% property_name = property[:name] %>
18
+ <% property_pretty_name = property[:pretty_name] %>
19
+ <th class="<%= sort == property_name.to_s ? sort_reverse ? 'sorted descending' : 'sorted ascending' : nil %>">
20
+ <a href="?<%= Merb::Parse.params_to_query_string(params.merge(:sort => property_name).reject{|key, value| key.to_sym == :sort_reverse}.merge(sort == property_name.to_s && sort_reverse != "true" ? {:sort_reverse => "true"} : {})) %>"><%= property_pretty_name %></a>
19
21
  </th>
20
22
  <% end %>
21
23
  </tr>
@@ -24,33 +26,9 @@
24
26
  <% @objects.each_with_index do |object, index| %>
25
27
  <tr class="row<%= index % 2 == 0 ? '1' : '2' %>">
26
28
  <% @properties.each do |property| %>
27
- <% type = property[:type] %>
28
- <% name = property[:name] %>
29
29
  <td>
30
30
  <a href="<%= url(:admin_edit, :model_name => @abstract_model.singular_name, :id => object.id) %>">
31
- <% case type %>
32
- <% when :boolean %>
33
- <% if object.send(name) == true %>
34
- <img alt="True" src="<%= image_path("icon-yes.gif") %>"/>
35
- <% else %>
36
- <img alt="False" src="<%= image_path("icon-no.gif") %>"/>
37
- <% end %>
38
- <% when :date_time %>
39
- <% value = object.send(name) %>
40
- <%= value.respond_to?(:strftime) ? value.strftime("%b. %d, %Y, %I:%M%p") : nil %>
41
- <% when :date %>
42
- <% value = object.send(name) %>
43
- <%= value.respond_to?(:strftime) ? value.strftime("%b. %d, %Y") : nil %>
44
- <% when :time %>
45
- <% value = object.send(name) %>
46
- <%= value.respond_to?(:strftime) ? value.strftime("%I:%M%p") : nil %>
47
- <% when :string %>
48
- <%= object.send(name).to_s.truncate(50) %>
49
- <% when :text %>
50
- <%= object.send(name).to_s.truncate(50) %>
51
- <% else %>
52
- <%= object.send(name) %>
53
- <% end %>
31
+ <%= object_property(object, property) %>
54
32
  </a>
55
33
  </td>
56
34
  <% end %>
@@ -1,21 +1,24 @@
1
1
  module MerbAdmin
2
2
  class AbstractModel
3
3
  module ActiverecordSupport
4
- def count(options = {})
5
- model.count(options)
6
- end
7
-
8
4
  def get(id)
9
5
  model.find(id).extend(InstanceMethods)
10
- rescue
6
+ rescue ActiveRecord::RecordNotFound
11
7
  nil
12
8
  end
13
9
 
10
+ def count(options = {})
11
+ merge_order!(options)
12
+ model.count(options)
13
+ end
14
+
14
15
  def first(options = {})
16
+ merge_order!(options)
15
17
  model.first(options).extend(InstanceMethods)
16
18
  end
17
19
 
18
20
  def all(options = {})
21
+ merge_order!(options)
19
22
  model.all(options)
20
23
  end
21
24
 
@@ -93,7 +96,6 @@ module MerbAdmin
93
96
  :length => property.limit,
94
97
  :nullable? => property.null,
95
98
  :serial? => property.primary,
96
- :key? => property.primary,
97
99
  :flag_map => property.type.respond_to?(:flag_map) ? property.type.flag_map : nil,
98
100
  }
99
101
  end
@@ -101,6 +103,12 @@ module MerbAdmin
101
103
 
102
104
  private
103
105
 
106
+ def merge_order!(options)
107
+ @sort ||= options.delete(:sort) || "id"
108
+ @sort_order ||= options.delete(:sort_reverse) ? "desc" : "asc"
109
+ options.merge!(:order => ["#{@sort} #{@sort_order}"])
110
+ end
111
+
104
112
  def association_parent_model_lookup(association)
105
113
  case association.macro
106
114
  when :belongs_to
@@ -1,19 +1,22 @@
1
1
  module MerbAdmin
2
2
  class AbstractModel
3
3
  module DatamapperSupport
4
- def count(options = {})
5
- model.count(options)
6
- end
7
-
8
4
  def get(id)
9
5
  model.get(id).extend(InstanceMethods)
10
6
  end
11
7
 
8
+ def count(options = {})
9
+ merge_order!(options)
10
+ model.count(options)
11
+ end
12
+
12
13
  def first(options = {})
14
+ merge_order!(options)
13
15
  model.first(options).extend(InstanceMethods)
14
16
  end
15
17
 
16
18
  def all(options = {})
19
+ merge_order!(options)
17
20
  model.all(options)
18
21
  end
19
22
 
@@ -70,7 +73,7 @@ module MerbAdmin
70
73
  model.relationships.to_a.map do |name, association|
71
74
  {
72
75
  :name => name,
73
- :pretty_name => name.to_s.gsub('_', ' ').capitalize,
76
+ :pretty_name => name.to_s.gsub("_", " ").capitalize,
74
77
  :type => association_type_lookup(association),
75
78
  :parent_model => association.parent_model,
76
79
  :parent_key => association.parent_key.map{|r| r.name},
@@ -84,12 +87,11 @@ module MerbAdmin
84
87
  model.properties.map do |property|
85
88
  {
86
89
  :name => property.name,
87
- :pretty_name => property.name.to_s.gsub('_', ' ').capitalize,
90
+ :pretty_name => property.name.to_s.gsub(/_id$/, "").gsub("_", " ").capitalize,
88
91
  :type => type_lookup(property),
89
92
  :length => property.length,
90
93
  :nullable? => property.nullable?,
91
94
  :serial? => property.serial?,
92
- :key? => property.key?,
93
95
  :flag_map => property.type.respond_to?(:flag_map) ? property.type.flag_map : nil,
94
96
  }
95
97
  end
@@ -97,6 +99,12 @@ module MerbAdmin
97
99
 
98
100
  private
99
101
 
102
+ def merge_order!(options)
103
+ @sort ||= options.delete(:sort) || :id
104
+ @sort_order ||= options.delete(:sort_reverse) ? :desc : :asc
105
+ options.merge!(:order => [@sort.to_sym.send(@sort_order)])
106
+ end
107
+
100
108
  def association_type_lookup(association)
101
109
  if self.model == association.parent_model
102
110
  association.options[:max] > 1 ? :has_many : :has_one
data/lib/merb-admin.rb CHANGED
@@ -23,7 +23,7 @@ if defined?(Merb::Plugins)
23
23
 
24
24
  # Slice metadata
25
25
  self.description = "MerbAdmin is a Merb plugin that provides an easy-to-use interface for managing your data."
26
- self.version = "0.4.3"
26
+ self.version = "0.4.4"
27
27
  self.author = "Erik Michaels-Ober"
28
28
 
29
29
  # Stub classes loaded hook - runs before LoadClasses BootLoader
@@ -89,6 +89,38 @@ describe "MerbAdmin" do
89
89
  end
90
90
  end
91
91
 
92
+ describe "list with sort" do
93
+ before(:each) do
94
+ MerbAdmin::AbstractModel.new("Player").create(:team_id => rand(99999), :number => 42, :name => "Jackie Robinson", :sex => :male, :position => :second)
95
+ MerbAdmin::AbstractModel.new("Player").create(:team_id => rand(99999), :number => 32, :name => "Sandy Koufax", :sex => :male, :position => :pitcher)
96
+ @response = request(url(:admin_list, :model_name => "player"), :params => {:sort => "name"})
97
+ end
98
+
99
+ it "should respond sucessfully" do
100
+ @response.should be_successful
101
+ end
102
+
103
+ it "should be sorted correctly" do
104
+ @response.body.should contain(/Jackie Robinson.*Sandy Koufax/m)
105
+ end
106
+ end
107
+
108
+ describe "list with reverse sort" do
109
+ before(:each) do
110
+ MerbAdmin::AbstractModel.new("Player").create(:team_id => rand(99999), :number => 42, :name => "Jackie Robinson", :sex => :male, :position => :second)
111
+ MerbAdmin::AbstractModel.new("Player").create(:team_id => rand(99999), :number => 32, :name => "Sandy Koufax", :sex => :male, :position => :pitcher)
112
+ @response = request(url(:admin_list, :model_name => "player"), :params => {:sort => "name", :sort_reverse => "true"})
113
+ end
114
+
115
+ it "should respond sucessfully" do
116
+ @response.should be_successful
117
+ end
118
+
119
+ it "should be sorted correctly" do
120
+ @response.body.should contain(/Sandy Koufax.*Jackie Robinson/m)
121
+ end
122
+ end
123
+
92
124
  describe "list with 2 objects", :given => "two players exist" do
93
125
  before(:each) do
94
126
  MerbAdmin[:paginate] = true
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: merb-admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Michaels-Ober
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-09-22 00:00:00 -07:00
12
+ date: 2009-09-29 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency