sferik-merb-admin 0.2.7 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +8 -3
- data/Rakefile +1 -1
- data/app/controllers/main.rb +91 -57
- data/app/helpers/main_helper.rb +3 -0
- data/app/views/layout/form.html.erb +2 -2
- data/app/views/layout/list.html.erb +2 -2
- data/app/views/main/_big_decimal.html.erb +2 -2
- data/app/views/main/_boolean.html.erb +3 -0
- data/app/views/main/_date.html.erb +3 -3
- data/app/views/main/_date_time.html.erb +3 -3
- data/app/views/main/_float.html.erb +2 -2
- data/app/views/main/_has_many.html.erb +10 -0
- data/app/views/main/_integer.html.erb +5 -5
- data/app/views/main/_string.html.erb +4 -12
- data/app/views/main/_text.html.erb +6 -0
- data/app/views/main/_time.html.erb +3 -3
- data/app/views/main/delete.html.erb +3 -3
- data/app/views/main/edit.html.erb +11 -8
- data/app/views/main/index.html.erb +4 -4
- data/app/views/main/list.html.erb +35 -41
- data/app/views/main/new.html.erb +9 -6
- data/lib/abstract_model.rb +171 -0
- data/lib/merb-admin.rb +2 -2
- data/lib/metaid.rb +19 -0
- data/public/javascripts/SelectFilter2.js +2 -2
- data/spec/fixtures/player_fixture.rb +2 -1
- data/spec/requests/main_spec.rb +54 -78
- metadata +7 -3
- data/app/views/main/_true_class.html.erb +0 -3
data/README.markdown
CHANGED
@@ -14,8 +14,8 @@ At the command prompt, type:
|
|
14
14
|
|
15
15
|
In your app, add the following dependency to `config/dependencies.rb`:
|
16
16
|
|
17
|
-
dependency "sferik-merb-admin", "0.2.
|
18
|
-
dependency "dm-is-paginated", "0.0.1" #
|
17
|
+
dependency "sferik-merb-admin", "0.2.8", :require_as => "merb-admin"
|
18
|
+
dependency "dm-is-paginated", "0.0.1" # if you want pagination support
|
19
19
|
|
20
20
|
Add the following route to `config/router.rb`:
|
21
21
|
|
@@ -51,6 +51,11 @@ MerbAdmin does not implement any authorization scheme. Make sure to apply author
|
|
51
51
|
|
52
52
|
## Acknowledgements
|
53
53
|
|
54
|
-
Many thanks to
|
54
|
+
Many thanks to:
|
55
|
+
|
56
|
+
* [Wilson Miner](http://www.wilsonminer.com) for contributing the stylesheets and javascripts from [Django](http://www.djangoproject.com)
|
57
|
+
* [Lori Holden](http://loriholden.com/) for providing pagination via [dm-is-paginated](http://github.com/lholden/dm-is-paginated)
|
58
|
+
* [Aaron Wheeler](http://fightinjoe.com/) for contributing libraries from [Merb AutoScaffold](http://github.com/fightinjoe/merb-autoscaffold)
|
59
|
+
* [why the lucky stiff](http://whytheluckystiff.net/) for [metaid](http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html)
|
55
60
|
|
56
61
|
Also, thanks to [beer](http://www.anchorbrewing.com).
|
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ AUTHOR = "Erik Michaels-Ober"
|
|
12
12
|
EMAIL = "sferik@gmail.com"
|
13
13
|
HOMEPAGE = "http://twitter.com/sferik"
|
14
14
|
SUMMARY = "MerbAdmin is a Merb plugin that provides an easy-to-use interface for managing your data."
|
15
|
-
GEM_VERSION = "0.2.
|
15
|
+
GEM_VERSION = "0.2.8"
|
16
16
|
|
17
17
|
spec = Gem::Specification.new do |s|
|
18
18
|
s.rubyforge_project = "merb"
|
data/app/controllers/main.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__), '..', '..', 'lib', 'abstract_model' )
|
2
|
+
require File.join( File.dirname(__FILE__), '..', '..', 'lib', 'metaid' )
|
3
|
+
|
1
4
|
class MerbAdmin::Main < MerbAdmin::Application
|
2
5
|
|
3
6
|
before :find_models, :only => ['index']
|
@@ -10,54 +13,36 @@ class MerbAdmin::Main < MerbAdmin::Application
|
|
10
13
|
|
11
14
|
def list
|
12
15
|
options = {}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
elsif @model.properties[key].primitive.to_s == "Integer" && @model.properties[key].type.respond_to?(:flag_map)
|
18
|
-
options.merge!(key.to_sym => value.to_sym)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
if params[:query]
|
22
|
-
condition_statement = []
|
23
|
-
conditions = []
|
24
|
-
@properties.each do |property|
|
25
|
-
next unless property.type.to_s == "String"
|
26
|
-
condition_statement << "#{property.field} LIKE ?"
|
27
|
-
conditions << "%#{params[:query]}%"
|
28
|
-
end
|
29
|
-
conditions.unshift(condition_statement.join(" OR "))
|
30
|
-
options.merge!(:conditions => conditions) unless conditions == [""]
|
31
|
-
end
|
32
|
-
if params[:sort]
|
33
|
-
order = "[:#{params[:sort]}.#{params[:sort_reverse] ? 'desc' : 'asc'}]"
|
34
|
-
options.merge!(:order => eval(order))
|
35
|
-
end
|
16
|
+
merge_filter(options)
|
17
|
+
merge_query(options)
|
18
|
+
merge_sort(options)
|
19
|
+
|
36
20
|
if !MerbAdmin[:paginate] || params[:all]
|
37
21
|
options = {
|
38
22
|
:limit => 200,
|
39
23
|
}.merge(options)
|
40
|
-
@objects = @
|
24
|
+
@objects = @abstract_model.find_all(options).reverse
|
41
25
|
else
|
42
26
|
# monkey patch pagination
|
43
|
-
@model.class_eval("is_paginated") unless @model.respond_to?(:paginated)
|
27
|
+
@abstract_model.model.class_eval("is_paginated") unless @abstract_model.model.respond_to?(:paginated)
|
44
28
|
@current_page = (params[:page] || 1).to_i
|
45
29
|
options = {
|
46
30
|
:page => @current_page,
|
47
31
|
:per_page => MerbAdmin[:per_page],
|
48
32
|
}.merge(options)
|
49
|
-
@page_count, @objects = @model.paginated(options)
|
33
|
+
@page_count, @objects = @abstract_model.model.paginated(options)
|
34
|
+
options.delete(:page)
|
35
|
+
options.delete(:per_page)
|
36
|
+
options.delete(:offset)
|
37
|
+
options.delete(:limit)
|
50
38
|
end
|
51
|
-
|
52
|
-
|
53
|
-
options.delete(:offset)
|
54
|
-
options.delete(:limit)
|
55
|
-
@record_count = @model.count(options)
|
39
|
+
|
40
|
+
@record_count = @abstract_model.count(options)
|
56
41
|
render(:layout => "list")
|
57
42
|
end
|
58
43
|
|
59
44
|
def new
|
60
|
-
@object = @
|
45
|
+
@object = @abstract_model.new
|
61
46
|
render(:layout => "form")
|
62
47
|
end
|
63
48
|
|
@@ -66,34 +51,44 @@ class MerbAdmin::Main < MerbAdmin::Application
|
|
66
51
|
end
|
67
52
|
|
68
53
|
def create
|
69
|
-
object =
|
70
|
-
|
71
|
-
|
54
|
+
object = params[@abstract_model.singular_name] || {}
|
55
|
+
# Delete fields that are blank
|
56
|
+
object.each do |key, value|
|
57
|
+
object.delete(key) if value.blank?
|
58
|
+
end
|
59
|
+
associations = @abstract_model.has_many_associations.map{|association| [association, (params[:associations] || {}).delete(association[:name])]}
|
60
|
+
@object = @abstract_model.new(object)
|
61
|
+
if @object.save && associations.each{|association, ids| update_has_many_association(association, ids)}
|
72
62
|
if params[:_continue]
|
73
|
-
redirect(slice_url(:admin_edit, :model_name => @
|
63
|
+
redirect(slice_url(:admin_edit, :model_name => @abstract_model.singular_name, :id => @object.id), :message => {:notice => "#{@abstract_model.pretty_name.capitalize} was successfully created"})
|
74
64
|
elsif params[:_add_another]
|
75
|
-
redirect(slice_url(:admin_new, :model_name => @
|
65
|
+
redirect(slice_url(:admin_new, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name.capitalize} was successfully created"})
|
76
66
|
else
|
77
|
-
redirect(slice_url(:admin_list, :model_name => @
|
67
|
+
redirect(slice_url(:admin_list, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name.capitalize} was successfully created"})
|
78
68
|
end
|
79
69
|
else
|
80
|
-
message[:error] = "#{@
|
70
|
+
message[:error] = "#{@abstract_model.pretty_name.capitalize} failed to be created"
|
81
71
|
render(:new, :layout => "form")
|
82
72
|
end
|
83
73
|
end
|
84
74
|
|
85
75
|
def update
|
86
|
-
object =
|
87
|
-
|
76
|
+
object = params[@abstract_model.singular_name] || {}
|
77
|
+
# Delete fields that are blank
|
78
|
+
object.each do |key, value|
|
79
|
+
object.delete(key) if value.blank?
|
80
|
+
end
|
81
|
+
associations = @abstract_model.has_many_associations.map{|association| [association, (params[:associations] || {}).delete(association[:name])]}
|
82
|
+
if @object.update_attributes(object) && associations.each{|association, ids| update_has_many_association(association, ids)}
|
88
83
|
if params[:_continue]
|
89
|
-
redirect(slice_url(:admin_edit, :model_name => @
|
84
|
+
redirect(slice_url(:admin_edit, :model_name => @abstract_model.singular_name, :id => @object.id), :message => {:notice => "#{@abstract_model.pretty_name.capitalize} was successfully updated"})
|
90
85
|
elsif params[:_add_another]
|
91
|
-
redirect(slice_url(:admin_new, :model_name => @
|
86
|
+
redirect(slice_url(:admin_new, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name.capitalize} was successfully updated"})
|
92
87
|
else
|
93
|
-
redirect(slice_url(:admin_list, :model_name => @
|
88
|
+
redirect(slice_url(:admin_list, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name.capitalize} was successfully updated"})
|
94
89
|
end
|
95
90
|
else
|
96
|
-
message[:error] = "#{@
|
91
|
+
message[:error] = "#{@abstract_model.pretty_name.capitalize} failed to be updated"
|
97
92
|
render(:edit, :layout => "form")
|
98
93
|
end
|
99
94
|
end
|
@@ -104,7 +99,7 @@ class MerbAdmin::Main < MerbAdmin::Application
|
|
104
99
|
|
105
100
|
def destroy
|
106
101
|
if @object.destroy
|
107
|
-
redirect(slice_url(:admin_list, :model_name => @
|
102
|
+
redirect(slice_url(:admin_list, :model_name => @abstract_model.singular_name), :message => {:notice => "#{@abstract_model.pretty_name.capitalize} was successfully destroyed"})
|
108
103
|
else
|
109
104
|
raise BadRequest
|
110
105
|
end
|
@@ -113,28 +108,67 @@ class MerbAdmin::Main < MerbAdmin::Application
|
|
113
108
|
private
|
114
109
|
|
115
110
|
def find_models
|
116
|
-
@
|
117
|
-
# remove DataMapperSessionStore because it's included by default
|
118
|
-
@models -= [Merb::DataMapperSessionStore] if Merb.const_defined?(:DataMapperSessionStore)
|
111
|
+
@abstract_models = MerbAdmin::AbstractModel.all
|
119
112
|
end
|
120
113
|
|
121
114
|
def find_model
|
122
|
-
|
123
|
-
|
124
|
-
@model = eval(@model_name)
|
125
|
-
rescue StandardError
|
126
|
-
raise NotFound
|
127
|
-
end
|
115
|
+
model_name = params[:model_name].camel_case
|
116
|
+
@abstract_model = MerbAdmin::AbstractModel.new(model_name)
|
128
117
|
find_properties
|
129
118
|
end
|
130
119
|
|
131
120
|
def find_properties
|
132
|
-
@properties = @
|
121
|
+
@properties = @abstract_model.properties
|
133
122
|
end
|
134
123
|
|
135
124
|
def find_object
|
136
|
-
@object = @
|
125
|
+
@object = @abstract_model.find(params[:id])
|
137
126
|
raise NotFound unless @object
|
138
127
|
end
|
139
128
|
|
129
|
+
def merge_filter(options)
|
130
|
+
return unless params[:filter]
|
131
|
+
params[:filter].each_pair do |key, value|
|
132
|
+
@properties.each do |property|
|
133
|
+
next unless property[:name] == key.to_sym
|
134
|
+
if property[:type] == :boolean
|
135
|
+
options.merge!(key.to_sym => (value == "true"))
|
136
|
+
elsif property[:type] == :integer && property[:flag_map]
|
137
|
+
options.merge!(key.to_sym => value.to_sym)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def merge_query(options)
|
144
|
+
return unless params[:query]
|
145
|
+
condition_statement = []
|
146
|
+
conditions = []
|
147
|
+
@properties.each do |property|
|
148
|
+
next unless property[:type] == :string
|
149
|
+
condition_statement << "#{property[:field]} LIKE ?"
|
150
|
+
conditions << "%#{params[:query]}%"
|
151
|
+
end
|
152
|
+
conditions.unshift(condition_statement.join(" OR "))
|
153
|
+
options.merge!(:conditions => conditions) unless conditions == [""]
|
154
|
+
end
|
155
|
+
|
156
|
+
def merge_sort(options)
|
157
|
+
return unless params[:sort]
|
158
|
+
options.merge!(:order => [params[:sort].to_sym.send(params[:sort_reverse] ? :desc : :asc)])
|
159
|
+
end
|
160
|
+
|
161
|
+
def update_has_many_association(association, ids)
|
162
|
+
# Remove all of the associated items
|
163
|
+
relationship = @object.send(association[:name])
|
164
|
+
@object.clear_association(relationship)
|
165
|
+
# Add all of the objects to the relationship
|
166
|
+
conditions = {association[:parent_key].first => ids}
|
167
|
+
model = MerbAdmin::AbstractModel.new(association[:child_model])
|
168
|
+
for object in model.find_all(conditions)
|
169
|
+
relationship << object
|
170
|
+
end
|
171
|
+
@object.save
|
172
|
+
end
|
173
|
+
|
140
174
|
end
|
data/app/helpers/main_helper.rb
CHANGED
@@ -2,6 +2,9 @@ require 'builder'
|
|
2
2
|
module Merb
|
3
3
|
module MerbAdmin
|
4
4
|
module MainHelper
|
5
|
+
def object_title(object)
|
6
|
+
object.try(:name) || object.try(:title) || "#{object.class.to_s} ##{object.id}"
|
7
|
+
end
|
5
8
|
|
6
9
|
# Given a page count and the current page, we generate a set of pagination
|
7
10
|
# links.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%
|
2
2
|
slice_name = "MerbAdmin" + (MerbAdmin[:app_name].blank? ? "" : " for #{MerbAdmin[:app_name]}")
|
3
|
-
page_name = action_name.capitalize + " " + @
|
3
|
+
page_name = action_name.capitalize + " " + @abstract_model.pretty_name
|
4
4
|
%>
|
5
5
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
6
6
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
|
@@ -35,7 +35,7 @@
|
|
35
35
|
</div>
|
36
36
|
<div class="breadcrumbs">
|
37
37
|
<%= link_to("Home", url(:admin_dashboard)) %> ›
|
38
|
-
<%= link_to(@
|
38
|
+
<%= link_to(@abstract_model.plural_name.to_s.capitalize, url(:admin_list, :model_name => @abstract_model.singular_name)) %> ›
|
39
39
|
<%= page_name %>
|
40
40
|
</div>
|
41
41
|
<%= partial('layout/message', :message => message) unless message.blank? -%>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%
|
2
2
|
slice_name = "MerbAdmin" + (MerbAdmin[:app_name].blank? ? "" : " for #{MerbAdmin[:app_name]}")
|
3
|
-
page_name = "Select " + @
|
3
|
+
page_name = "Select " + @abstract_model.pretty_name + " to edit"
|
4
4
|
%>
|
5
5
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
6
6
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
|
@@ -30,7 +30,7 @@
|
|
30
30
|
</div>
|
31
31
|
<div class="breadcrumbs">
|
32
32
|
<%= link_to("Home", url(:admin_dashboard)) %> ›
|
33
|
-
<%=
|
33
|
+
<%= @abstract_model.plural_name.to_s.capitalize %>
|
34
34
|
</div>
|
35
35
|
<%= partial('layout/message', :message => message) unless message.blank? -%>
|
36
36
|
<div id="content" class="flex">
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<div>
|
2
|
-
<%= text_field(property
|
2
|
+
<%= text_field(property[:name], :maxlength => property[:length], :label => property[:pretty_name].capitalize) %>
|
3
3
|
<p class="help">
|
4
|
-
<%= !property
|
4
|
+
<%= !property[:nullable?] || property[:serial?] ? "Required." : "Optional." %>
|
5
5
|
</p>
|
6
6
|
</div>
|
@@ -1,7 +1,7 @@
|
|
1
|
-
<% value =
|
1
|
+
<% value = @object.send(property[:name]) %>
|
2
2
|
<div>
|
3
|
-
<%= text_field(property
|
3
|
+
<%= text_field(property[:name], :class => "vDateField", :label => property[:pretty_name].capitalize, :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : nil) %>
|
4
4
|
<p class="help">
|
5
|
-
<%= !property
|
5
|
+
<%= !property[:nullable?] ? "Required." : "Optional." %>
|
6
6
|
</p>
|
7
7
|
</div>
|
@@ -1,7 +1,7 @@
|
|
1
|
-
<% value =
|
1
|
+
<% value = @object.send(property[:name]) %>
|
2
2
|
<div>
|
3
|
-
<%= text_field(property
|
3
|
+
<%= text_field(property[:name], :class => "vDateField", :label => property[:pretty_name].capitalize, :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S") : nil) %>
|
4
4
|
<p class="help">
|
5
|
-
<%= !property
|
5
|
+
<%= !property[:nullable?] ? "Required." : "Optional." %>
|
6
6
|
</p>
|
7
7
|
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<div>
|
2
|
-
<%= text_field(property
|
2
|
+
<%= text_field(property[:name], :maxlength => property[:length], :label => property[:pretty_name].capitalize) %>
|
3
3
|
<p class="help">
|
4
|
-
<%= !property
|
4
|
+
<%= !property[:nullable?] || property[:serial?] ? "Required." : "Optional." %>
|
5
5
|
</p>
|
6
6
|
</div>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<fieldset class="module aligned">
|
2
|
+
<h2><%= association[:pretty_name].capitalize %></h2>
|
3
|
+
<div class="form-row">
|
4
|
+
<div>
|
5
|
+
<%= select(:name => "associations[#{association[:name]}][]", :id => association[:name], :collection => MerbAdmin::AbstractModel.new(association[:child_model]).find_all.map{|o| [o.id, object_title(o)]}.sort_by{|o| o[1]}, :selected => @object.send(association[:name]).map{|o| o.id.to_s}, :label => association[:pretty_name].capitalize, :multiple => true) %>
|
6
|
+
<script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("<%= association[:name] %>", "<%= association[:name] %>", 0, "<%= image_path %>"); });</script>
|
7
|
+
<p class="help">Hold down "Control", or "Command" on a Mac, to select more than one.</p>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
</fieldset>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<div>
|
2
|
-
<% if property
|
3
|
-
<%= select(property
|
2
|
+
<% if property[:flag_map] # Enum or Flag type %>
|
3
|
+
<%= select(property[:name], :collection => property[:flag_map].map{|x| [x[1], x[1].to_s.capitalize.gsub('_', ' ')]}.sort{|a, b| a[1] <=> b[1]}, :label => property[:pretty_name].capitalize) %>
|
4
4
|
<% else %>
|
5
|
-
<%= text_field(property
|
6
|
-
<% end %>
|
5
|
+
<%= text_field(property[:name], :maxlength => property[:length], :label => property[:pretty_name].capitalize) %>
|
7
6
|
<p class="help">
|
8
|
-
<%= !property
|
7
|
+
<%= !property[:nullable?] || property[:serial?] ? "Required." : "Optional." %>
|
9
8
|
</p>
|
9
|
+
<% end %>
|
10
10
|
</div>
|
@@ -1,14 +1,6 @@
|
|
1
1
|
<div>
|
2
|
-
|
3
|
-
|
4
|
-
<%=
|
5
|
-
|
6
|
-
<%= property.nullable? ? "Required." : "Optional." %>
|
7
|
-
</p>
|
8
|
-
<% else %>
|
9
|
-
<%= text_field(property.name, :size => [50, property.length].min, :maxlength => property.length, :label => property.field.capitalize.gsub('_', ' ')) %>
|
10
|
-
<p class="help">
|
11
|
-
<%= !property.nullable? ? "Required." : "Optional." %> <%= property.length %> <%= property.length == 1 ? "character." : "characters or fewer." %>
|
12
|
-
</p>
|
13
|
-
<% end %>
|
2
|
+
<%= text_field(property[:name], :size => [50, property[:length]].min, :maxlength => property[:length], :label => property[:pretty_name].capitalize) %>
|
3
|
+
<p class="help">
|
4
|
+
<%= !property[:nullable?] ? "Required." : "Optional." %> <%= property[:length] %> <%= property[:length] == 1 ? "character." : "characters or fewer." %>
|
5
|
+
</p>
|
14
6
|
</div>
|
@@ -1,7 +1,7 @@
|
|
1
|
-
<% value =
|
1
|
+
<% value = @object.send(property[:name]) %>
|
2
2
|
<div>
|
3
|
-
<%= text_field(property
|
3
|
+
<%= text_field(property[:name], :class => "vTimeField", :label => property[:pretty_name].capitalize, :value => value.respond_to?(:strftime) ? value.strftime("%H:%M:%S") : nil) %>
|
4
4
|
<p class="help">
|
5
|
-
<%= !property
|
5
|
+
<%= !property[:nullable?] ? "Required." : "Optional." %>
|
6
6
|
</p>
|
7
7
|
</div>
|
@@ -1,10 +1,10 @@
|
|
1
|
-
<p>Are you sure you want to delete the <%= @
|
1
|
+
<p>Are you sure you want to delete the <%= @abstract_model.pretty_name %>? All of the following related items will be deleted:</p>
|
2
2
|
<ul>
|
3
3
|
<li>
|
4
|
-
<%= link_to(@model_name, slice_url(:admin_edit, :model_name => @
|
4
|
+
<%= link_to(@model_name, slice_url(:admin_edit, :model_name => @abstract_model.singular_name, :id => @object.id))%>
|
5
5
|
</li>
|
6
6
|
</ul>
|
7
|
-
<%= form_for(@object, :action => slice_url(:admin_destroy, :model_name => @
|
7
|
+
<%= form_for(@object, :action => slice_url(:admin_destroy, :model_name => @abstract_model.singular_name, :id => @object.id), :method => :delete) do %>
|
8
8
|
<div>
|
9
9
|
<%= submit "Yes, I'm sure" %>
|
10
10
|
</div>
|
@@ -1,30 +1,33 @@
|
|
1
1
|
<div id="content-main">
|
2
2
|
<ul class="object-tools">
|
3
3
|
<li>
|
4
|
-
<%= link_to("View on site", "/#{@
|
4
|
+
<%= link_to("View on site", "/#{@abstract_model.singular_name}/#{@object.id}", :target => "_blank", :class => "viewsitelink") %>
|
5
5
|
</li>
|
6
6
|
</ul>
|
7
|
-
<%= form_for(@object, :action => slice_url(:admin_update, :model_name => @
|
7
|
+
<%= form_for(@object, :action => slice_url(:admin_update, :model_name => @abstract_model.singular_name, :id => @object.id)) do %>
|
8
8
|
<div>
|
9
9
|
<fieldset class="module aligned">
|
10
10
|
<% @properties.each do |property| %>
|
11
|
-
|
12
|
-
<div class="<%= @object.errors[property
|
13
|
-
<% if @object.errors[property
|
11
|
+
<% next if [:id, :created_at, :created_on, :updated_at, :updated_on].include?(property[:name]) %>
|
12
|
+
<div class="<%= @object.errors[property[:name]] ? "form-row errors" : "form-row"%>">
|
13
|
+
<% if @object.errors[property[:name]] %>
|
14
14
|
<ul class="errorlist">
|
15
|
-
<% @object.errors[property
|
15
|
+
<% @object.errors[property[:name]].each do |error| %>
|
16
16
|
<li><%= error %></li>
|
17
17
|
<% end %>
|
18
18
|
</ul>
|
19
19
|
<% end %>
|
20
|
-
<%= partial(property.
|
20
|
+
<%= partial(property[:type].to_s, :property => property) -%>
|
21
21
|
</div>
|
22
22
|
<% end %>
|
23
23
|
</fieldset>
|
24
|
+
<% @abstract_model.has_many_associations.each do |association| %>
|
25
|
+
<%= partial('has_many', :association => association) %>
|
26
|
+
<% end %>
|
24
27
|
<div class="submit-row" >
|
25
28
|
<%= submit "Save", :class => "default", :name => "_save" %>
|
26
29
|
<p class="deletelink-box">
|
27
|
-
<%= link_to("Delete", slice_url(:admin_delete, :model_name => @
|
30
|
+
<%= link_to("Delete", slice_url(:admin_delete, :model_name => @abstract_model.singular_name, :id => @object.id), :class => "deletelink") %>
|
28
31
|
</p>
|
29
32
|
<%= submit "Save and add another", :name => "_add_another" %>
|
30
33
|
<%= submit "Save and continue editing", :name => "_continue" %>
|
@@ -4,16 +4,16 @@
|
|
4
4
|
<caption>
|
5
5
|
<%= link_to("Models", slice_url(:admin_dashboard), :class => "section") %>
|
6
6
|
</caption>
|
7
|
-
<% @
|
7
|
+
<% @abstract_models.each do |abstract_model| %>
|
8
8
|
<tr>
|
9
9
|
<th scope="row">
|
10
|
-
<%= link_to(
|
10
|
+
<%= link_to(abstract_model.plural_name.to_s.capitalize, slice_url(:admin_list, :model_name => abstract_model.singular_name)) %>
|
11
11
|
</th>
|
12
12
|
<td>
|
13
|
-
<%= link_to("Add", slice_url(:admin_new, :model_name =>
|
13
|
+
<%= link_to("Add", slice_url(:admin_new, :model_name => abstract_model.singular_name), :class => "addlink") %>
|
14
14
|
</td>
|
15
15
|
<td>
|
16
|
-
<%= link_to("Edit", slice_url(:admin_list, :model_name =>
|
16
|
+
<%= link_to("Edit", slice_url(:admin_list, :model_name => abstract_model.singular_name), :class => "changelink") %>
|
17
17
|
</td>
|
18
18
|
</tr>
|
19
19
|
<% end %>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<div id="content-main">
|
3
3
|
<ul class="object-tools">
|
4
4
|
<li>
|
5
|
-
<%= link_to("Add #{@
|
5
|
+
<%= link_to("Add #{@abstract_model.pretty_name}", slice_url(:admin_new, :model_name => @abstract_model.singular_name), :class => "addlink") %>
|
6
6
|
</li>
|
7
7
|
</ul>
|
8
8
|
<div class="module filtered" id="changelist">
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<input type="text" size="40" name="query" value="" id="searchbar" />
|
14
14
|
<input type="submit" value="Search" />
|
15
15
|
<% if params[:query] || params[:filter] %>
|
16
|
-
<span class="small quiet"><%= @record_count %> <%= @record_count == 1 ? "result" : "results" %> (<a href="?"><%= @
|
16
|
+
<span class="small quiet"><%= @record_count %> <%= @record_count == 1 ? "result" : "results" %> (<a href="?"><%= @abstract_model.count %> total</a>)</span>
|
17
17
|
<% end %>
|
18
18
|
<% if params[:filter] %>
|
19
19
|
<% params[:filter].each do |name, value| %>
|
@@ -27,28 +27,28 @@
|
|
27
27
|
<div id="changelist-filter">
|
28
28
|
<h2>Filter</h2>
|
29
29
|
<% @properties.each do |property| %>
|
30
|
-
<% if property
|
31
|
-
<h3>By <%= property
|
30
|
+
<% if property[:type] == :boolean %>
|
31
|
+
<h3>By <%= property[:pretty_name] %></h3>
|
32
32
|
<ul>
|
33
|
-
<li class="<%= params[:filter].nil? || params[:filter][property
|
34
|
-
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).reject{|key, value| key.to_sym == property
|
33
|
+
<li class="<%= params[:filter].nil? || params[:filter][property[:name]].blank? ? "selected" : nil %>">
|
34
|
+
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).reject{|key, value| key.to_sym == property[:name]})) %>">All</a>
|
35
35
|
</li>
|
36
|
-
<li class="<%= params[:filter] && params[:filter][property
|
37
|
-
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).merge({property
|
36
|
+
<li class="<%= params[:filter] && params[:filter][property[:name]] == "true" ? "selected" : nil %>">
|
37
|
+
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).merge({property[:name] => "true"}))) %>">Yes</a>
|
38
38
|
</li>
|
39
|
-
<li class="<%= params[:filter] && params[:filter][property
|
40
|
-
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).merge({property
|
39
|
+
<li class="<%= params[:filter] && params[:filter][property[:name]] == "false" ? "selected" : nil %>">
|
40
|
+
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).merge({property[:name] => "false"}))) %>">No</a>
|
41
41
|
</li>
|
42
42
|
</ul>
|
43
|
-
<% elsif property
|
44
|
-
<h3>By <%= property
|
43
|
+
<% elsif property[:type] == :integer && property[:flag_map] %>
|
44
|
+
<h3>By <%= property[:pretty_name] %></h3>
|
45
45
|
<ul>
|
46
|
-
<li class="<%= params[:filter].nil? || params[:filter][property
|
47
|
-
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).reject{|key, value| key.to_sym == property
|
46
|
+
<li class="<%= params[:filter].nil? || params[:filter][property[:name]].blank? ? "selected" : nil %>">
|
47
|
+
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).reject{|key, value| key.to_sym == property[:name]})) %>">All</a>
|
48
48
|
</li>
|
49
|
-
<% property
|
50
|
-
<li class="<%= params[:filter] && params[:filter][property
|
51
|
-
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).merge({property
|
49
|
+
<% property[:flag_map].each do |value, name| %>
|
50
|
+
<li class="<%= params[:filter] && params[:filter][property[:name]] == name.to_s ? "selected" : nil %>">
|
51
|
+
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:filter => (params[:filter] || {}).merge({property[:name] => name}))) %>"><%= name.to_s.capitalize.gsub('_', ' ')%></a>
|
52
52
|
</li>
|
53
53
|
<% end %>
|
54
54
|
</ul>
|
@@ -59,8 +59,8 @@
|
|
59
59
|
<thead>
|
60
60
|
<tr>
|
61
61
|
<% @properties.each do |property| %>
|
62
|
-
<th class="<%= params[:sort] == property
|
63
|
-
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:sort => property
|
62
|
+
<th class="<%= params[:sort] == property[:field] ? params[:sort_reverse] ? 'sorted descending' : 'sorted ascending' : nil %>">
|
63
|
+
<a href="?<%= Merb::Parse.params_to_query_string(params.merge(:sort => property[:field]).reject{|key, value| key.to_sym == :sort_reverse}.merge(params[:sort] == property[:field] && params[:sort_reverse] != "true" ? {:sort_reverse => "true"} : {})) %>"><%= property[:pretty_name].capitalize %></a>
|
64
64
|
</th>
|
65
65
|
<% end %>
|
66
66
|
</tr>
|
@@ -70,35 +70,29 @@
|
|
70
70
|
<tr class="row<%= index % 2 == 0 ? '1' : '2' %>">
|
71
71
|
<% @properties.each do |property| %>
|
72
72
|
<td>
|
73
|
-
<a href="<%= slice_url(:admin_edit, :model_name => @
|
74
|
-
<% case property
|
75
|
-
<% when
|
76
|
-
<% if
|
73
|
+
<a href="<%= slice_url(:admin_edit, :model_name => @abstract_model.singular_name, :id => object.id) %>">
|
74
|
+
<% case property[:type] %>
|
75
|
+
<% when :boolean %>
|
76
|
+
<% if object.send(property[:name]) == true %>
|
77
77
|
<img alt="True" src="<%= image_path("icon-yes.gif") %>"/>
|
78
78
|
<% else %>
|
79
79
|
<img alt="False" src="<%= image_path("icon-no.gif") %>"/>
|
80
80
|
<% end %>
|
81
|
-
<% when
|
82
|
-
|
81
|
+
<% when :date_time %>
|
82
|
+
<% value = object.send(property[:name]) %>
|
83
83
|
<%= value.respond_to?(:strftime) ? value.strftime("%b. %d, %Y, %I:%M%p") : nil %>
|
84
|
-
<% when
|
85
|
-
|
84
|
+
<% when :date %>
|
85
|
+
<% value = object.send(property[:name]) %>
|
86
86
|
<%= value.respond_to?(:strftime) ? value.strftime("%b. %d, %Y") : nil %>
|
87
|
-
<% when
|
88
|
-
|
87
|
+
<% when :time %>
|
88
|
+
<% value = object.send(property[:name]) %>
|
89
89
|
<%= value.respond_to?(:strftime) ? value.strftime("%I:%M%p") : nil %>
|
90
|
-
<% when
|
91
|
-
|
92
|
-
|
90
|
+
<% when :string %>
|
91
|
+
<%= object.send(property[:name]).to_s.truncate(50) %>
|
92
|
+
<% when :text %>
|
93
|
+
<%= object.send(property[:name]).to_s.truncate(50) %>
|
93
94
|
<% else %>
|
94
|
-
<%=
|
95
|
-
<% end %>
|
96
|
-
<% when "BigDecimal" %>
|
97
|
-
<%= eval("object.#{property.field}") %>
|
98
|
-
<% when "Float" %>
|
99
|
-
<%= eval("object.#{property.field}") %>
|
100
|
-
<% when "String" %>
|
101
|
-
<%= eval("object.#{property.field}").to_s.truncate(50) %>
|
95
|
+
<%= object.send(property[:name]) %>
|
102
96
|
<% end %>
|
103
97
|
</a>
|
104
98
|
</td>
|
@@ -111,7 +105,7 @@
|
|
111
105
|
<% if @page_count.to_i > 1 %>
|
112
106
|
<%= paginate(@current_page, @page_count, :url => '?' + Merb::Parse.params_to_query_string(params)) %>
|
113
107
|
<% end %>
|
114
|
-
<%= @record_count %> <%= @record_count == 1 ? @
|
108
|
+
<%= @record_count %> <%= @record_count == 1 ? @abstract_model.pretty_name : @abstract_model.pretty_name.pluralize %>
|
115
109
|
<% if @page_count.to_i == 2 %>
|
116
110
|
<%= link_to("Show all", '?' + Merb::Parse.params_to_query_string(params.merge(:all => true)), :class => "showall") %>
|
117
111
|
<% end %>
|
data/app/views/main/new.html.erb
CHANGED
@@ -1,21 +1,24 @@
|
|
1
1
|
<div id="content-main">
|
2
|
-
<%= form_for(@object, :action => slice_url(:admin_create, :model_name => @
|
2
|
+
<%= form_for(@object, :action => slice_url(:admin_create, :model_name => @abstract_model.singular_name)) do %>
|
3
3
|
<div>
|
4
4
|
<fieldset class="module aligned">
|
5
5
|
<% @properties.each do |property| %>
|
6
|
-
<% next if [:id, :created_at, :created_on, :updated_at, :updated_on].include?(property
|
7
|
-
<div class="<%= @object.errors[property
|
8
|
-
<% if @object.errors[property
|
6
|
+
<% next if [:id, :created_at, :created_on, :updated_at, :updated_on].include?(property[:name]) %>
|
7
|
+
<div class="<%= @object.errors[property[:name]] ? "form-row errors" : "form-row"%>">
|
8
|
+
<% if @object.errors[property[:name]] %>
|
9
9
|
<ul class="errorlist">
|
10
|
-
<% @object.errors[property
|
10
|
+
<% @object.errors[property[:name]].each do |error| %>
|
11
11
|
<li><%= error %></li>
|
12
12
|
<% end %>
|
13
13
|
</ul>
|
14
14
|
<% end %>
|
15
|
-
<%= partial(property.
|
15
|
+
<%= partial(property[:type].to_s, :property => property) -%>
|
16
16
|
</div>
|
17
17
|
<% end %>
|
18
18
|
</fieldset>
|
19
|
+
<% @abstract_model.has_many_associations.each do |association| %>
|
20
|
+
<%= partial('has_many', :association => association) %>
|
21
|
+
<% end %>
|
19
22
|
<div class="submit-row" >
|
20
23
|
<%= submit "Save", :class => "default", :name => "_save" %>
|
21
24
|
<%= submit "Save and add another", :name => "_add_another" %>
|
@@ -0,0 +1,171 @@
|
|
1
|
+
module MerbAdmin
|
2
|
+
class AbstractModel
|
3
|
+
|
4
|
+
# Returns all models for a given Merb app
|
5
|
+
def self.all
|
6
|
+
return @models if @models
|
7
|
+
@models ||= []
|
8
|
+
case Merb.orm
|
9
|
+
when :datamapper
|
10
|
+
DataMapper::Resource.descendants.each do |m|
|
11
|
+
# Remove DataMapperSessionStore because it's included by default
|
12
|
+
next if m == Merb::DataMapperSessionStore if Merb.const_defined?(:DataMapperSessionStore)
|
13
|
+
model = lookup(m.to_s.to_sym)
|
14
|
+
@models << new(model) if model
|
15
|
+
end
|
16
|
+
@models.sort{|a, b| a.to_s <=> b.to_s}
|
17
|
+
else
|
18
|
+
raise "MerbAdmin does not currently support the #{Merb.orm} ORM"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Given a symbol +model_name+, finds the corresponding model class
|
23
|
+
def self.lookup(model_name)
|
24
|
+
model = const_get(model_name)
|
25
|
+
raise "could not find model #{model_name}" if model.nil?
|
26
|
+
return model if model.include?(DataMapper::Resource)
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_accessor :model
|
31
|
+
|
32
|
+
def initialize(model)
|
33
|
+
model = self.class.lookup(model.camel_case.to_sym) unless model.is_a?(Class)
|
34
|
+
@model = model
|
35
|
+
self.extend(GenericSupport)
|
36
|
+
case Merb.orm
|
37
|
+
when :datamapper
|
38
|
+
self.extend(DatamapperSupport)
|
39
|
+
else
|
40
|
+
raise "MerbAdmin does not currently support the #{Merb.orm} ORM"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module GenericSupport
|
45
|
+
def singular_name
|
46
|
+
model.to_s.snake_case.to_sym
|
47
|
+
end
|
48
|
+
|
49
|
+
def plural_name
|
50
|
+
model.to_s.snake_case.pluralize.to_sym
|
51
|
+
end
|
52
|
+
|
53
|
+
def pretty_name
|
54
|
+
model.to_s.snake_case.gsub('_', ' ')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module DatamapperSupport
|
59
|
+
def count(options = {})
|
60
|
+
model.count(options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_all(options = {})
|
64
|
+
model.all(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def find(id)
|
68
|
+
model.get(id).extend(InstanceMethods)
|
69
|
+
end
|
70
|
+
|
71
|
+
def new(params = {})
|
72
|
+
model.new(params).extend(InstanceMethods)
|
73
|
+
end
|
74
|
+
|
75
|
+
def has_many_associations
|
76
|
+
associations.select do |association|
|
77
|
+
association[:type] == :has_many
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_one_associations
|
82
|
+
associations.select do |association|
|
83
|
+
association[:type] == :has_one
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def belongs_to_associations
|
88
|
+
associations.select do |association|
|
89
|
+
association[:type] == :belongs_to
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def associations
|
94
|
+
model.relationships.to_a.map do |name, relationship|
|
95
|
+
{
|
96
|
+
:name => name,
|
97
|
+
:pretty_name => name.to_s.gsub('_', ' '),
|
98
|
+
:type => association_type_lookup(relationship),
|
99
|
+
:parent_model => relationship.parent_model,
|
100
|
+
:parent_key => relationship.parent_key.map{|p| p.name},
|
101
|
+
:child_model => relationship.child_model,
|
102
|
+
:child_key => relationship.child_key.map{|p| p.name},
|
103
|
+
:remote_relationship => relationship.options[:remote_relationship_name],
|
104
|
+
:near_relationship => relationship.options[:near_relationship_name],
|
105
|
+
}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def association_names
|
110
|
+
associations.map do |association|
|
111
|
+
association[:type] == :belongs_to ? association[:parent_name] : association[:child_name]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def properties
|
116
|
+
model.properties.map do |property|
|
117
|
+
{
|
118
|
+
:name => property.name,
|
119
|
+
:pretty_name => property.field.gsub('_', ' '),
|
120
|
+
:nullable? => property.nullable?,
|
121
|
+
:serial? => property.serial?,
|
122
|
+
:key? => property.key?,
|
123
|
+
:field => property.field,
|
124
|
+
:length => property.length,
|
125
|
+
:type => type_lookup(property),
|
126
|
+
:flag_map => property.type.respond_to?(:flag_map) ? property.type.flag_map : nil,
|
127
|
+
}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def association_type_lookup(relationship)
|
134
|
+
if self.model == relationship.parent_model
|
135
|
+
relationship.options[:max] > 1 ? :has_many : :has_one
|
136
|
+
elsif self.model == relationship.child_model
|
137
|
+
:belongs_to
|
138
|
+
else
|
139
|
+
raise "Unknown association type"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def type_lookup(property)
|
144
|
+
type = {
|
145
|
+
DataMapper::Types::Serial => :integer,
|
146
|
+
DataMapper::Types::Boolean => :boolean,
|
147
|
+
DataMapper::Types::Text => :text,
|
148
|
+
DataMapper::Types::ParanoidDateTime => :date_time,
|
149
|
+
Integer => :integer,
|
150
|
+
Fixnum => :integer,
|
151
|
+
Float => :float,
|
152
|
+
BigDecimal => :big_decimal,
|
153
|
+
TrueClass => :boolean,
|
154
|
+
String => :string,
|
155
|
+
DateTime => :date_time,
|
156
|
+
Date => :date,
|
157
|
+
Time => :time,
|
158
|
+
}
|
159
|
+
type[property.type] || type[property.primitive]
|
160
|
+
end
|
161
|
+
|
162
|
+
module InstanceMethods
|
163
|
+
def clear_association(association)
|
164
|
+
association.clear
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
data/lib/merb-admin.rb
CHANGED
@@ -22,8 +22,8 @@ if defined?(Merb::Plugins)
|
|
22
22
|
module MerbAdmin
|
23
23
|
|
24
24
|
# Slice metadata
|
25
|
-
self.description = "MerbAdmin is a
|
26
|
-
self.version = "0.2.
|
25
|
+
self.description = "MerbAdmin is a Merb plugin that provides an easy-to-use interface for managing your data."
|
26
|
+
self.version = "0.2.8"
|
27
27
|
self.author = "Erik Michaels-Ober"
|
28
28
|
|
29
29
|
# Stub classes loaded hook - runs before LoadClasses BootLoader
|
data/lib/metaid.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Object
|
2
|
+
# The hidden singleton lurks behind everyone
|
3
|
+
def metaclass; class << self; self; end; end
|
4
|
+
def meta_eval &blk; metaclass.instance_eval &blk; end
|
5
|
+
|
6
|
+
# Adds methods to a metaclass
|
7
|
+
def meta_def name, &blk
|
8
|
+
meta_eval { define_method name, &blk }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Defines an instance method within a class
|
12
|
+
def class_def name, &blk
|
13
|
+
class_eval { define_method name, &blk }
|
14
|
+
end
|
15
|
+
|
16
|
+
def try( method )
|
17
|
+
self.send( method ) if self.respond_to?( method )
|
18
|
+
end
|
19
|
+
end
|
@@ -36,7 +36,7 @@ var SelectFilter = {
|
|
36
36
|
quickElement('h2', selector_available, interpolate(gettext('Available %s'), [field_name]));
|
37
37
|
var filter_p = quickElement('p', selector_available, '');
|
38
38
|
filter_p.className = 'selector-filter';
|
39
|
-
quickElement('img', filter_p, '', 'src', admin_media_prefix + '
|
39
|
+
quickElement('img', filter_p, '', 'src', admin_media_prefix + '/selector-search.gif');
|
40
40
|
filter_p.appendChild(document.createTextNode(' '));
|
41
41
|
var filter_input = quickElement('input', filter_p, '', 'type', 'text');
|
42
42
|
filter_input.id = field_id + '_input';
|
@@ -58,7 +58,7 @@ var SelectFilter = {
|
|
58
58
|
quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s'), [field_name]));
|
59
59
|
var selector_filter = quickElement('p', selector_chosen, gettext('Select your choice(s) and click '));
|
60
60
|
selector_filter.className = 'selector-filter';
|
61
|
-
quickElement('img', selector_filter, '', 'src', admin_media_prefix + (is_stacked ? '
|
61
|
+
quickElement('img', selector_filter, '', 'src', admin_media_prefix + (is_stacked ? '/selector_stacked-add.gif':'/selector-add.gif'), 'alt', 'Add');
|
62
62
|
var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
|
63
63
|
to_box.className = 'filtered';
|
64
64
|
var clear_all = quickElement('a', selector_chosen, gettext('Clear all'), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + '_to", "' + field_id + '_from");})()');
|
@@ -8,7 +8,7 @@ class Player
|
|
8
8
|
property :team_id, Integer, :nullable => false, :index => true
|
9
9
|
property :number, Integer, :nullable => false
|
10
10
|
property :name, String, :length => 100, :nullable => false
|
11
|
-
property :position,
|
11
|
+
property :position, Enum[:pitcher, :catcher, :first, :second, :third, :shortstop, :left, :center, :right]
|
12
12
|
property :sex, Enum[:male, :female]
|
13
13
|
property :batting_average, Float, :default => 0.0, :precision => 4, :scale => 3
|
14
14
|
property :injured, Boolean, :default => false
|
@@ -24,5 +24,6 @@ Player.fixture {{
|
|
24
24
|
:team_id => /\d{1,2}/.gen,
|
25
25
|
:number => /\d{1,2}/.gen,
|
26
26
|
:name => "#{/\w{3,10}/.gen.capitalize} #{/\w{5,10}/.gen.capitalize}",
|
27
|
+
:position => Player.properties[:position].type.flag_map.values[rand(Player.properties[:position].type.flag_map.length)],
|
27
28
|
:sex => Player.properties[:sex].type.flag_map.values[rand(Player.properties[:sex].type.flag_map.length)],
|
28
29
|
}}
|
data/spec/requests/main_spec.rb
CHANGED
@@ -28,7 +28,7 @@ describe "MerbAdmin" do
|
|
28
28
|
@response.should be_successful
|
29
29
|
end
|
30
30
|
|
31
|
-
it "should
|
31
|
+
it "should contain \"Site administration\"" do
|
32
32
|
@response.body.should contain("Site administration")
|
33
33
|
end
|
34
34
|
end
|
@@ -43,7 +43,7 @@ describe "MerbAdmin" do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it "should contain \"Select model to edit\"" do
|
46
|
-
@response.body.should contain("Select
|
46
|
+
@response.body.should contain("Select player to edit")
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
@@ -119,27 +119,7 @@ describe "MerbAdmin" do
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
describe "list with
|
123
|
-
before(:each) do
|
124
|
-
Player.gen(:name => "Jackie Robinson", :sex => :male)
|
125
|
-
Player.gen(:name => "Dottie Hinson", :sex => :female)
|
126
|
-
@response = request(url(:admin_list, :model_name => "player"), :params => {:filter => {:sex => :male}})
|
127
|
-
end
|
128
|
-
|
129
|
-
it "should respond sucessfully" do
|
130
|
-
@response.should be_successful
|
131
|
-
end
|
132
|
-
|
133
|
-
it "should contain matching results" do
|
134
|
-
@response.body.should contain("Jackie Robinson")
|
135
|
-
end
|
136
|
-
|
137
|
-
it "should not contain non-matching results" do
|
138
|
-
@response.body.should_not contain("Dottie Hinson")
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
describe "list with 2 players", :given => "two players exist" do
|
122
|
+
describe "list with 2 objects", :given => "two players exist" do
|
143
123
|
before(:each) do
|
144
124
|
MerbAdmin[:paginate] = true
|
145
125
|
MerbAdmin[:per_page] = 1
|
@@ -151,72 +131,68 @@ describe "MerbAdmin" do
|
|
151
131
|
end
|
152
132
|
|
153
133
|
it "should contain \"2 results\"" do
|
154
|
-
@response.body.should contain("2
|
134
|
+
@response.body.should contain("2 players")
|
155
135
|
end
|
156
136
|
end
|
157
137
|
|
158
|
-
describe "list with
|
138
|
+
describe "list with 20 objects", :given => "twenty players exist" do
|
159
139
|
before(:each) do
|
160
140
|
MerbAdmin[:paginate] = true
|
161
141
|
MerbAdmin[:per_page] = 1
|
162
|
-
@response = request(url(:admin_list, :model_name => "player")
|
142
|
+
@response = request(url(:admin_list, :model_name => "player"))
|
163
143
|
end
|
164
144
|
|
165
145
|
it "should respond sucessfully" do
|
166
146
|
@response.should be_successful
|
167
147
|
end
|
168
148
|
|
169
|
-
it "should contain \"
|
170
|
-
@response.body.should contain("
|
149
|
+
it "should contain \"20 results\"" do
|
150
|
+
@response.body.should contain("20 players")
|
171
151
|
end
|
172
152
|
end
|
173
153
|
|
174
|
-
describe "list with 20
|
154
|
+
describe "list with 20 objects, page 8", :given => "twenty players exist" do
|
175
155
|
before(:each) do
|
176
156
|
MerbAdmin[:paginate] = true
|
177
157
|
MerbAdmin[:per_page] = 1
|
178
|
-
@response = request(url(:admin_list, :model_name => "player"))
|
158
|
+
@response = request(url(:admin_list, :model_name => "player"), :params => {:page => 8})
|
179
159
|
end
|
180
160
|
|
181
161
|
it "should respond sucessfully" do
|
182
162
|
@response.should be_successful
|
183
163
|
end
|
184
164
|
|
185
|
-
it "should
|
186
|
-
@response.body.should contain(
|
165
|
+
it "should paginate correctly" do
|
166
|
+
@response.body.should contain(/1 2[^0-9]*5 6 7 8 9 10 11[^0-9]*19 20/)
|
187
167
|
end
|
188
168
|
end
|
189
169
|
|
190
|
-
describe "list with 20
|
170
|
+
describe "list with 20 objects, page 17", :given => "twenty players exist" do
|
191
171
|
before(:each) do
|
192
172
|
MerbAdmin[:paginate] = true
|
193
173
|
MerbAdmin[:per_page] = 1
|
194
|
-
@response = request(url(:admin_list, :model_name => "player"), :params => {:page =>
|
174
|
+
@response = request(url(:admin_list, :model_name => "player"), :params => {:page => 17})
|
195
175
|
end
|
196
176
|
|
197
177
|
it "should respond sucessfully" do
|
198
178
|
@response.should be_successful
|
199
179
|
end
|
200
180
|
|
201
|
-
it "should
|
202
|
-
@response.body.should contain(
|
181
|
+
it "should paginate correctly" do
|
182
|
+
@response.body.should contain(/1 2[^0-9]*12 13 14 15 16 17 18 19 20/)
|
203
183
|
end
|
204
184
|
end
|
205
185
|
|
206
|
-
describe "list
|
186
|
+
describe "list show all", :given => "two players exist" do
|
207
187
|
before(:each) do
|
208
188
|
MerbAdmin[:paginate] = true
|
209
189
|
MerbAdmin[:per_page] = 1
|
210
|
-
@response = request(url(:admin_list, :model_name => "player"), :params => {:
|
190
|
+
@response = request(url(:admin_list, :model_name => "player"), :params => {:all => true})
|
211
191
|
end
|
212
192
|
|
213
193
|
it "should respond sucessfully" do
|
214
194
|
@response.should be_successful
|
215
195
|
end
|
216
|
-
|
217
|
-
it "should contain \"20 results\"" do
|
218
|
-
@response.body.should contain("20 #{"player".gsub('_', ' ').pluralize}")
|
219
|
-
end
|
220
196
|
end
|
221
197
|
|
222
198
|
describe "new" do
|
@@ -229,7 +205,7 @@ describe "MerbAdmin" do
|
|
229
205
|
end
|
230
206
|
|
231
207
|
it "should contain \"New model\"" do
|
232
|
-
@response.body.should contain("New
|
208
|
+
@response.body.should contain("New player")
|
233
209
|
end
|
234
210
|
end
|
235
211
|
|
@@ -243,7 +219,7 @@ describe "MerbAdmin" do
|
|
243
219
|
end
|
244
220
|
|
245
221
|
it "should contain \"Edit model\"" do
|
246
|
-
@response.body.should contain("Edit
|
222
|
+
@response.body.should contain("Edit player")
|
247
223
|
end
|
248
224
|
end
|
249
225
|
|
@@ -259,7 +235,7 @@ describe "MerbAdmin" do
|
|
259
235
|
|
260
236
|
describe "create" do
|
261
237
|
before(:each) do
|
262
|
-
@response = request(url(:admin_create, :model_name => "player"), :method => "post", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :sex => :male}})
|
238
|
+
@response = request(url(:admin_create, :model_name => "player"), :method => "post", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :position => :second, :sex => :male}})
|
263
239
|
end
|
264
240
|
|
265
241
|
it "should redirect to list" do
|
@@ -271,19 +247,9 @@ describe "MerbAdmin" do
|
|
271
247
|
end
|
272
248
|
end
|
273
249
|
|
274
|
-
describe "create with invalid object" do
|
275
|
-
before(:each) do
|
276
|
-
@response = request(url(:admin_create, :model_name => "player"), :method => "post", :params => {:player => {}})
|
277
|
-
end
|
278
|
-
|
279
|
-
it "should have an error message" do
|
280
|
-
@response.body.should contain("Player failed to be created")
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
250
|
describe "create and edit" do
|
285
251
|
before(:each) do
|
286
|
-
@response = request(url(:admin_create, :model_name => "player"), :method => "post", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :sex => :male}, :_continue => true})
|
252
|
+
@response = request(url(:admin_create, :model_name => "player"), :method => "post", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :position => :second, :sex => :male}, :_continue => true})
|
287
253
|
end
|
288
254
|
|
289
255
|
it "should redirect to edit" do
|
@@ -297,7 +263,7 @@ describe "MerbAdmin" do
|
|
297
263
|
|
298
264
|
describe "create and add another" do
|
299
265
|
before(:each) do
|
300
|
-
@response = request(url(:admin_create, :model_name => "player"), :method => "post", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :sex => :male}, :_add_another => true})
|
266
|
+
@response = request(url(:admin_create, :model_name => "player"), :method => "post", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :position => :second, :sex => :male}, :_add_another => true})
|
301
267
|
end
|
302
268
|
|
303
269
|
it "should redirect to new" do
|
@@ -309,37 +275,27 @@ describe "MerbAdmin" do
|
|
309
275
|
end
|
310
276
|
end
|
311
277
|
|
312
|
-
describe "
|
278
|
+
describe "create with invalid object" do
|
313
279
|
before(:each) do
|
314
|
-
@response = request(url(:
|
315
|
-
end
|
316
|
-
|
317
|
-
it "should redirect to list" do
|
318
|
-
@response.should redirect_to(url(:admin_list, :model_name => "player"))
|
280
|
+
@response = request(url(:admin_create, :model_name => "player"), :method => "post", :params => {:player => {}})
|
319
281
|
end
|
320
282
|
|
321
|
-
it "should
|
322
|
-
|
283
|
+
it "should contain an error message" do
|
284
|
+
@response.body.should contain("Player failed to be created")
|
323
285
|
end
|
324
286
|
end
|
325
287
|
|
326
|
-
describe "update
|
288
|
+
describe "update", :given => "an object exists" do
|
327
289
|
before(:each) do
|
328
|
-
@response = request(url(:admin_update, :model_name => "player", :id => @player.id), :method => "put", :params => {:player => {:number =>
|
329
|
-
end
|
330
|
-
|
331
|
-
it "should have an error message" do
|
332
|
-
@response.body.should contain("Player failed to be updated")
|
290
|
+
@response = request(url(:admin_update, :model_name => "player", :id => @player.id), :method => "put", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :sex => :male}})
|
333
291
|
end
|
334
|
-
end
|
335
292
|
|
336
|
-
|
337
|
-
|
338
|
-
@response = request(url(:admin_update, :model_name => "player", :id => 1), :method => "put", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :sex => :male}})
|
293
|
+
it "should redirect to list" do
|
294
|
+
@response.should redirect_to(url(:admin_list, :model_name => "player"))
|
339
295
|
end
|
340
296
|
|
341
|
-
it "should
|
342
|
-
@
|
297
|
+
it "should update an object that already exists" do
|
298
|
+
Player.first(:id => @player.id).name.should eql("Jackie Robinson")
|
343
299
|
end
|
344
300
|
end
|
345
301
|
|
@@ -371,6 +327,26 @@ describe "MerbAdmin" do
|
|
371
327
|
end
|
372
328
|
end
|
373
329
|
|
330
|
+
describe "update with missing object" do
|
331
|
+
before(:each) do
|
332
|
+
@response = request(url(:admin_update, :model_name => "player", :id => 1), :method => "put", :params => {:player => {:name => "Jackie Robinson", :number => 42, :team_id => 1, :sex => :male}})
|
333
|
+
end
|
334
|
+
|
335
|
+
it "should raise NotFound" do
|
336
|
+
@response.status.should == 404
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
describe "update with invalid object", :given => "an object exists" do
|
341
|
+
before(:each) do
|
342
|
+
@response = request(url(:admin_update, :model_name => "player", :id => @player.id), :method => "put", :params => {:player => {:number => "a"}})
|
343
|
+
end
|
344
|
+
|
345
|
+
it "should contain an error message" do
|
346
|
+
@response.body.should contain("Player failed to be updated")
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
374
350
|
describe "delete", :given => "an object exists" do
|
375
351
|
before(:each) do
|
376
352
|
@response = request(url(:admin_delete, :model_name => "player", :id => @player.id))
|
@@ -381,7 +357,7 @@ describe "MerbAdmin" do
|
|
381
357
|
end
|
382
358
|
|
383
359
|
it "should contain \"Delete model\"" do
|
384
|
-
@response.body.should contain("Delete
|
360
|
+
@response.body.should contain("Delete player")
|
385
361
|
end
|
386
362
|
end
|
387
363
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sferik-merb-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
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-08-
|
12
|
+
date: 2009-08-24 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -55,11 +55,13 @@ files:
|
|
55
55
|
- LICENSE
|
56
56
|
- README.markdown
|
57
57
|
- Rakefile
|
58
|
+
- lib/abstract_model.rb
|
58
59
|
- lib/merb-admin
|
59
60
|
- lib/merb-admin/merbtasks.rb
|
60
61
|
- lib/merb-admin/slicetasks.rb
|
61
62
|
- lib/merb-admin/spectasks.rb
|
62
63
|
- lib/merb-admin.rb
|
64
|
+
- lib/metaid.rb
|
63
65
|
- spec/controllers
|
64
66
|
- spec/controllers/main_spec.rb
|
65
67
|
- spec/fixtures
|
@@ -85,13 +87,15 @@ files:
|
|
85
87
|
- app/views/layout/list.html.erb
|
86
88
|
- app/views/main
|
87
89
|
- app/views/main/_big_decimal.html.erb
|
90
|
+
- app/views/main/_boolean.html.erb
|
88
91
|
- app/views/main/_date.html.erb
|
89
92
|
- app/views/main/_date_time.html.erb
|
90
93
|
- app/views/main/_float.html.erb
|
94
|
+
- app/views/main/_has_many.html.erb
|
91
95
|
- app/views/main/_integer.html.erb
|
92
96
|
- app/views/main/_string.html.erb
|
97
|
+
- app/views/main/_text.html.erb
|
93
98
|
- app/views/main/_time.html.erb
|
94
|
-
- app/views/main/_true_class.html.erb
|
95
99
|
- app/views/main/delete.html.erb
|
96
100
|
- app/views/main/edit.html.erb
|
97
101
|
- app/views/main/index.html.erb
|