merb-admin 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.markdown +58 -0
- data/Rakefile +64 -0
- data/app/controllers/application.rb +6 -0
- data/app/controllers/main.rb +145 -0
- data/app/helpers/application_helper.rb +64 -0
- data/app/helpers/main_helper.rb +122 -0
- data/app/views/layout/_message.html.erb +10 -0
- data/app/views/layout/dashboard.html.erb +34 -0
- data/app/views/layout/form.html.erb +48 -0
- data/app/views/layout/list.html.erb +42 -0
- data/app/views/main/_belongs_to.html.erb +29 -0
- data/app/views/main/_big_decimal.html.erb +12 -0
- data/app/views/main/_boolean.html.erb +7 -0
- data/app/views/main/_date.html.erb +12 -0
- data/app/views/main/_datetime.html.erb +12 -0
- data/app/views/main/_float.html.erb +12 -0
- data/app/views/main/_has_many.html.erb +16 -0
- data/app/views/main/_has_one.html.erb +30 -0
- data/app/views/main/_integer.html.erb +18 -0
- data/app/views/main/_properties.html.erb +18 -0
- data/app/views/main/_string.html.erb +12 -0
- data/app/views/main/_text.html.erb +11 -0
- data/app/views/main/_time.html.erb +12 -0
- data/app/views/main/_timestamp.html.erb +12 -0
- data/app/views/main/delete.html.erb +12 -0
- data/app/views/main/edit.html.erb +25 -0
- data/app/views/main/index.html.erb +22 -0
- data/app/views/main/list.html.erb +72 -0
- data/app/views/main/new.html.erb +22 -0
- data/lib/abstract_model.rb +67 -0
- data/lib/activerecord_support.rb +169 -0
- data/lib/datamapper_support.rb +157 -0
- data/lib/generic_support.rb +17 -0
- data/lib/merb-admin/merbtasks.rb +103 -0
- data/lib/merb-admin/slicetasks.rb +20 -0
- data/lib/merb-admin/spectasks.rb +53 -0
- data/lib/merb-admin.rb +99 -0
- data/public/images/arrow-down.gif +0 -0
- data/public/images/arrow-up.gif +0 -0
- data/public/images/changelist-bg.gif +0 -0
- data/public/images/changelist-bg_rtl.gif +0 -0
- data/public/images/chooser-bg.gif +0 -0
- data/public/images/chooser_stacked-bg.gif +0 -0
- data/public/images/default-bg-reverse.gif +0 -0
- data/public/images/default-bg.gif +0 -0
- data/public/images/deleted-overlay.gif +0 -0
- data/public/images/icon-no.gif +0 -0
- data/public/images/icon-unknown.gif +0 -0
- data/public/images/icon-yes.gif +0 -0
- data/public/images/icon_addlink.gif +0 -0
- data/public/images/icon_alert.gif +0 -0
- data/public/images/icon_calendar.gif +0 -0
- data/public/images/icon_changelink.gif +0 -0
- data/public/images/icon_clock.gif +0 -0
- data/public/images/icon_deletelink.gif +0 -0
- data/public/images/icon_error.gif +0 -0
- data/public/images/icon_searchbox.png +0 -0
- data/public/images/icon_success.gif +0 -0
- data/public/images/inline-delete-8bit.png +0 -0
- data/public/images/inline-delete.png +0 -0
- data/public/images/inline-restore-8bit.png +0 -0
- data/public/images/inline-restore.png +0 -0
- data/public/images/inline-splitter-bg.gif +0 -0
- data/public/images/nav-bg-grabber.gif +0 -0
- data/public/images/nav-bg-reverse.gif +0 -0
- data/public/images/nav-bg.gif +0 -0
- data/public/images/selector-add.gif +0 -0
- data/public/images/selector-addall.gif +0 -0
- data/public/images/selector-remove.gif +0 -0
- data/public/images/selector-removeall.gif +0 -0
- data/public/images/selector-search.gif +0 -0
- data/public/images/selector_stacked-add.gif +0 -0
- data/public/images/selector_stacked-remove.gif +0 -0
- data/public/images/tool-left.gif +0 -0
- data/public/images/tool-left_over.gif +0 -0
- data/public/images/tool-right.gif +0 -0
- data/public/images/tool-right_over.gif +0 -0
- data/public/images/tooltag-add.gif +0 -0
- data/public/images/tooltag-add_over.gif +0 -0
- data/public/images/tooltag-arrowright.gif +0 -0
- data/public/images/tooltag-arrowright_over.gif +0 -0
- data/public/javascripts/CollapsedFieldsets.js +85 -0
- data/public/javascripts/DateTimeShortcuts.js +255 -0
- data/public/javascripts/RelatedObjectLookups.js +96 -0
- data/public/javascripts/SelectBox.js +111 -0
- data/public/javascripts/SelectFilter2.js +113 -0
- data/public/javascripts/actions.js +39 -0
- data/public/javascripts/calendar.js +143 -0
- data/public/javascripts/core.js +176 -0
- data/public/javascripts/dateparse.js +233 -0
- data/public/javascripts/getElementsBySelector.js +167 -0
- data/public/javascripts/i18n.js +33 -0
- data/public/javascripts/master.js +0 -0
- data/public/javascripts/ordering.js +137 -0
- data/public/javascripts/timeparse.js +94 -0
- data/public/javascripts/urlify.js +140 -0
- data/public/stylesheets/base.css +746 -0
- data/public/stylesheets/changelists.css +269 -0
- data/public/stylesheets/dashboard.css +24 -0
- data/public/stylesheets/forms.css +327 -0
- data/public/stylesheets/global.css +142 -0
- data/public/stylesheets/ie.css +51 -0
- data/public/stylesheets/layout.css +29 -0
- data/public/stylesheets/login.css +54 -0
- data/public/stylesheets/master.css +2 -0
- data/public/stylesheets/null.css +1 -0
- data/public/stylesheets/patch-iewin.css +8 -0
- data/public/stylesheets/rtl.css +206 -0
- data/public/stylesheets/widgets.css +506 -0
- data/schema/migrations/001_create_divisions_migration.rb +15 -0
- data/schema/migrations/002_create_drafts_migration.rb +21 -0
- data/schema/migrations/003_create_leagues_migration.rb +13 -0
- data/schema/migrations/004_create_players_migration.rb +23 -0
- data/schema/migrations/005_create_teams_migration.rb +18 -0
- data/spec/controllers/main_spec.rb +25 -0
- data/spec/models/activerecord/division.rb +8 -0
- data/spec/models/activerecord/draft.rb +16 -0
- data/spec/models/activerecord/league.rb +6 -0
- data/spec/models/activerecord/player.rb +10 -0
- data/spec/models/activerecord/team.rb +11 -0
- data/spec/models/datamapper/division.rb +12 -0
- data/spec/models/datamapper/draft.rb +18 -0
- data/spec/models/datamapper/league.rb +11 -0
- data/spec/models/datamapper/player.rb +21 -0
- data/spec/models/datamapper/team.rb +15 -0
- data/spec/requests/main_spec.rb +475 -0
- data/spec/spec_helper.rb +112 -0
- metadata +209 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
<%
|
2
|
+
name = property[:name]
|
3
|
+
value = @object.send(name)
|
4
|
+
label = property[:pretty_name]
|
5
|
+
required = !property[:nullable?]
|
6
|
+
%>
|
7
|
+
<div>
|
8
|
+
<%= text_field(name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : nil, :label => label) %>
|
9
|
+
<p class="help">
|
10
|
+
<%= required ? "Required." : "Optional." %>
|
11
|
+
</p>
|
12
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%
|
2
|
+
name = property[:name]
|
3
|
+
value = @object.send(name)
|
4
|
+
label = property[:pretty_name]
|
5
|
+
required = !property[:nullable?]
|
6
|
+
%>
|
7
|
+
<div>
|
8
|
+
<%= text_field(name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S") : nil, :label => label) %>
|
9
|
+
<p class="help">
|
10
|
+
<%= required ? "Required." : "Optional." %>
|
11
|
+
</p>
|
12
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%
|
2
|
+
name = property[:name]
|
3
|
+
length = property[:length]
|
4
|
+
label = property[:pretty_name]
|
5
|
+
required = !property[:nullable?] || property[:serial?]
|
6
|
+
%>
|
7
|
+
<div>
|
8
|
+
<%= text_field(name, :maxlength => length, :label => label) %>
|
9
|
+
<p class="help">
|
10
|
+
<%= required ? "Required." : "Optional." %>
|
11
|
+
</p>
|
12
|
+
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<%
|
2
|
+
name = association[:name]
|
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)
|
5
|
+
label = association[:pretty_name]
|
6
|
+
%>
|
7
|
+
<fieldset class="module aligned">
|
8
|
+
<h2><%= label %></h2>
|
9
|
+
<div class="form-row">
|
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>
|
13
|
+
<p class="help">Hold down "Control", or "Command" on a Mac, to select more than one.</p>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</fieldset>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<%
|
2
|
+
child_key = association[:child_key].first
|
3
|
+
name = association[:name]
|
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])
|
6
|
+
label = association[:pretty_name]
|
7
|
+
required = false
|
8
|
+
@properties.each do |property|
|
9
|
+
next unless property[:name] == child_key
|
10
|
+
required = true unless property[:nullable?]
|
11
|
+
end
|
12
|
+
%>
|
13
|
+
<fieldset class="module aligned">
|
14
|
+
<h2><%= label %></h2>
|
15
|
+
<div class="<%= @object.errors[child_key] ? "form-row errors" : "form-row" %>">
|
16
|
+
<% if @object.errors[child_key] %>
|
17
|
+
<ul class="errorlist">
|
18
|
+
<% @object.errors[child_key].each do |error| %>
|
19
|
+
<li><%= error %></li>
|
20
|
+
<% end %>
|
21
|
+
</ul>
|
22
|
+
<% end %>
|
23
|
+
<div>
|
24
|
+
<%= select(:name => "associations[#{name}][]", :id => name, :collection => collection, :include_blank => true, :selected => selected ? selected.id.to_s : nil, :label => label) %>
|
25
|
+
<p class="help">
|
26
|
+
<%= required ? "Required." : "Optional." %>
|
27
|
+
</p>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</fieldset>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%
|
2
|
+
flag_map = property[:flag_map]
|
3
|
+
name = property[:name]
|
4
|
+
label = property[:pretty_name]
|
5
|
+
required = !property[:nullable?] || property[:serial?]
|
6
|
+
%>
|
7
|
+
<div>
|
8
|
+
<% if flag_map # Enum or Flag type %>
|
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) %>
|
11
|
+
<% else %>
|
12
|
+
<% length = property[:length] %>
|
13
|
+
<%= text_field(name, :maxlength => length, :label => label) %>
|
14
|
+
<p class="help">
|
15
|
+
<%= required ? "Required." : "Optional." %>
|
16
|
+
</p>
|
17
|
+
<% end %>
|
18
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<fieldset class="module aligned">
|
2
|
+
<% belongs_to_keys = @abstract_model.belongs_to_associations.map{|b| b[:child_key].first} %>
|
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] %>
|
9
|
+
<ul class="errorlist">
|
10
|
+
<% @object.errors[name].each do |error| %>
|
11
|
+
<li><%= error %></li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
14
|
+
<% end %>
|
15
|
+
<%= partial(property[:type].to_s, :property => property) -%>
|
16
|
+
</div>
|
17
|
+
<% end %>
|
18
|
+
</fieldset>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%
|
2
|
+
name = property[:name]
|
3
|
+
length = property[:length]
|
4
|
+
label = property[:pretty_name]
|
5
|
+
required = !property[:nullable?]
|
6
|
+
%>
|
7
|
+
<div>
|
8
|
+
<%= text_field(name, :size => [50, length].min, :maxlength => length, :label => label) %>
|
9
|
+
<p class="help">
|
10
|
+
<%= required ? "Required." : "Optional." %> <%= length %> <%= length == 1 ? "character." : "characters or fewer." %>
|
11
|
+
</p>
|
12
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%
|
2
|
+
name = property[:name]
|
3
|
+
value = @object.send(name)
|
4
|
+
label = property[:pretty_name]
|
5
|
+
required = !property[:nullable?]
|
6
|
+
%>
|
7
|
+
<div>
|
8
|
+
<%= text_field(name, :class => "vTimeField", :value => value.respond_to?(:strftime) ? value.strftime("%H:%M:%S") : nil, :label => label) %>
|
9
|
+
<p class="help">
|
10
|
+
<%= required ? "Required." : "Optional." %>
|
11
|
+
</p>
|
12
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%
|
2
|
+
name = property[:name]
|
3
|
+
value = @object.send(name)
|
4
|
+
label = property[:pretty_name]
|
5
|
+
required = !property[:nullable?]
|
6
|
+
%>
|
7
|
+
<div>
|
8
|
+
<%= text_field(name, :class => "vDateField", :value => value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S") : nil, :label => label) %>
|
9
|
+
<p class="help">
|
10
|
+
<%= required ? "Required." : "Optional." %>
|
11
|
+
</p>
|
12
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<p>Are you sure you want to delete the <%= @abstract_model.pretty_name.downcase %>? All of the following related items will be deleted:</p>
|
2
|
+
<ul>
|
3
|
+
<li>
|
4
|
+
<%= link_to(@model_name, url(:admin_edit, :model_name => @abstract_model.singular_name, :id => @object.id))%>
|
5
|
+
</li>
|
6
|
+
</ul>
|
7
|
+
<%= form_for(@object, :action => url(:admin_destroy, :model_name => @abstract_model.singular_name, :id => @object.id), :method => :delete) do %>
|
8
|
+
<div>
|
9
|
+
<%= submit "Yes, I'm sure" %>
|
10
|
+
</div>
|
11
|
+
<% end =%>
|
12
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<div id="content-main">
|
2
|
+
<%= form_for(@object, :action => url(:admin_update, :model_name => @abstract_model.singular_name, :id => @object.id)) do %>
|
3
|
+
<div>
|
4
|
+
<%= partial('properties', :properties => @properties) -%>
|
5
|
+
<% @abstract_model.belongs_to_associations.each do |association| %>
|
6
|
+
<%= partial('belongs_to', :association => association) -%>
|
7
|
+
<% end %>
|
8
|
+
<% @abstract_model.has_one_associations.each do |association| %>
|
9
|
+
<%= partial('has_one', :association => association) -%>
|
10
|
+
<% end %>
|
11
|
+
<% @abstract_model.has_many_associations.each do |association| %>
|
12
|
+
<%= partial('has_many', :association => association) -%>
|
13
|
+
<% end %>
|
14
|
+
<div class="submit-row" >
|
15
|
+
<%= submit "Save", :class => "default", :name => "_save" %>
|
16
|
+
<p class="deletelink-box">
|
17
|
+
<%= link_to("Delete", url(:admin_delete, :model_name => @abstract_model.singular_name, :id => @object.id), :class => "deletelink") %>
|
18
|
+
</p>
|
19
|
+
<%= submit "Save and add another", :name => "_add_another" %>
|
20
|
+
<%= submit "Save and continue editing", :name => "_continue" %>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
<% end =%>
|
24
|
+
|
25
|
+
</div>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<div id="content-main">
|
2
|
+
<div class="module">
|
3
|
+
<table summary="Models available in the application.">
|
4
|
+
<caption>
|
5
|
+
<%= link_to("Models", url(:admin_dashboard), :class => "section") %>
|
6
|
+
</caption>
|
7
|
+
<% @abstract_models.each do |abstract_model| %>
|
8
|
+
<tr>
|
9
|
+
<th scope="row">
|
10
|
+
<%= link_to(abstract_model.plural_name.to_s.capitalize, url(:admin_list, :model_name => abstract_model.singular_name)) %>
|
11
|
+
</th>
|
12
|
+
<td>
|
13
|
+
<%= link_to("Add", url(:admin_new, :model_name => abstract_model.singular_name), :class => "addlink") %>
|
14
|
+
</td>
|
15
|
+
<td>
|
16
|
+
<%= link_to("Edit", url(:admin_list, :model_name => abstract_model.singular_name), :class => "changelink") %>
|
17
|
+
</td>
|
18
|
+
</tr>
|
19
|
+
<% end %>
|
20
|
+
</table>
|
21
|
+
</div>
|
22
|
+
</div>
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<%
|
2
|
+
params = request.params.except(:action, :controller, :model_name)
|
3
|
+
%>
|
4
|
+
<div id="content-main">
|
5
|
+
<ul class="object-tools">
|
6
|
+
<li>
|
7
|
+
<%= link_to("Add #{@abstract_model.pretty_name.downcase}", url(:admin_new, :model_name => @abstract_model.singular_name), :class => "addlink") %>
|
8
|
+
</li>
|
9
|
+
</ul>
|
10
|
+
<div class="module" id="changelist">
|
11
|
+
<table cellspacing="0">
|
12
|
+
<thead>
|
13
|
+
<tr>
|
14
|
+
<% @properties.each do |property| %>
|
15
|
+
<% name = property[:name] %>
|
16
|
+
<% pretty_name = property[:pretty_name] %>
|
17
|
+
<th>
|
18
|
+
<%= pretty_name %>
|
19
|
+
</th>
|
20
|
+
<% end %>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
<tbody>
|
24
|
+
<% @objects.each_with_index do |object, index| %>
|
25
|
+
<tr class="row<%= index % 2 == 0 ? '1' : '2' %>">
|
26
|
+
<% @properties.each do |property| %>
|
27
|
+
<% type = property[:type] %>
|
28
|
+
<% name = property[:name] %>
|
29
|
+
<td>
|
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 %>
|
54
|
+
</a>
|
55
|
+
</td>
|
56
|
+
<% end %>
|
57
|
+
</tr>
|
58
|
+
<% end %>
|
59
|
+
</tbody>
|
60
|
+
</table>
|
61
|
+
<p class="paginator">
|
62
|
+
<% if @page_count.to_i > 1 %>
|
63
|
+
<%= paginate(@current_page, @page_count, :url => '?' + Merb::Parse.params_to_query_string(params)) %>
|
64
|
+
<% end %>
|
65
|
+
<%= @record_count %> <%= @record_count == 1 ? @abstract_model.pretty_name.downcase : @abstract_model.pretty_name.downcase.pluralize %>
|
66
|
+
<% if @page_count.to_i == 2 %>
|
67
|
+
<%= link_to("Show all", '?' + Merb::Parse.params_to_query_string(params.merge(:all => true)), :class => "showall") %>
|
68
|
+
<% end %>
|
69
|
+
</p>
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
<br class="clear" />
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<div id="content-main">
|
2
|
+
<%= form_for(@object, :action => url(:admin_create, :model_name => @abstract_model.singular_name)) do %>
|
3
|
+
<div>
|
4
|
+
<%= partial('properties', :properties => @properties) -%>
|
5
|
+
<% @abstract_model.belongs_to_associations.each do |association| %>
|
6
|
+
<%= partial('belongs_to', :association => association) -%>
|
7
|
+
<% end %>
|
8
|
+
<% @abstract_model.has_one_associations.each do |association| %>
|
9
|
+
<%= partial('has_one', :association => association) -%>
|
10
|
+
<% end %>
|
11
|
+
<% @abstract_model.has_many_associations.each do |association| %>
|
12
|
+
<%= partial('has_many', :association => association) -%>
|
13
|
+
<% end %>
|
14
|
+
<div class="submit-row" >
|
15
|
+
<%= submit "Save", :class => "default", :name => "_save" %>
|
16
|
+
<%= submit "Save and add another", :name => "_add_another" %>
|
17
|
+
<%= submit "Save and continue editing", :name => "_continue" %>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
<% end =%>
|
21
|
+
|
22
|
+
</div>
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'generic_support'
|
2
|
+
require 'activerecord_support'
|
3
|
+
require 'datamapper_support'
|
4
|
+
|
5
|
+
module MerbAdmin
|
6
|
+
class AbstractModel
|
7
|
+
# Returns all models for a given Merb app
|
8
|
+
def self.all
|
9
|
+
return @models if @models
|
10
|
+
@models ||= []
|
11
|
+
case Merb.orm
|
12
|
+
when :activerecord
|
13
|
+
Dir.glob(Merb.dir_for(:model) / Merb.glob_for(:model)).each do |filename|
|
14
|
+
# FIXME: This heuristic for finding ActiveRecord models is too strict
|
15
|
+
File.read(filename).scan(/^class ([\w\d_\-:]+) < ActiveRecord::Base$/).flatten.each do |m|
|
16
|
+
model = lookup(m.to_s.to_sym)
|
17
|
+
@models << new(model) if model
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@models.sort!{|a, b| a.model.to_s <=> b.model.to_s}
|
21
|
+
when :datamapper
|
22
|
+
DataMapper::Resource.descendants.each do |m|
|
23
|
+
# Remove DataMapperSessionStore because it's included by default
|
24
|
+
next if m == Merb::DataMapperSessionStore if Merb.const_defined?(:DataMapperSessionStore)
|
25
|
+
model = lookup(m.to_s.to_sym)
|
26
|
+
@models << new(model) if model
|
27
|
+
end
|
28
|
+
@models.sort!{|a, b| a.model.to_s <=> b.model.to_s}
|
29
|
+
else
|
30
|
+
raise "MerbAdmin does not support the #{Merb.orm} ORM"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Given a symbol +model_name+, finds the corresponding model class
|
35
|
+
def self.lookup(model_name)
|
36
|
+
begin
|
37
|
+
model = const_get(model_name)
|
38
|
+
rescue NameError
|
39
|
+
raise "MerbAdmin could not find model #{model_name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
case Merb.orm
|
43
|
+
when :activerecord
|
44
|
+
return model if model.superclass == ActiveRecord::Base
|
45
|
+
when :datamapper
|
46
|
+
return model if model.include?(DataMapper::Resource)
|
47
|
+
end
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_accessor :model
|
52
|
+
|
53
|
+
def initialize(model)
|
54
|
+
model = self.class.lookup(model.to_s.camel_case) unless model.is_a?(Class)
|
55
|
+
@model = model
|
56
|
+
self.extend(GenericSupport)
|
57
|
+
case Merb.orm
|
58
|
+
when :activerecord
|
59
|
+
self.extend(ActiverecordSupport)
|
60
|
+
when :datamapper
|
61
|
+
self.extend(DatamapperSupport)
|
62
|
+
else
|
63
|
+
raise "MerbAdmin does not support the #{Merb.orm} ORM"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module MerbAdmin
|
2
|
+
class AbstractModel
|
3
|
+
module ActiverecordSupport
|
4
|
+
def count(options = {})
|
5
|
+
model.count(options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def get(id)
|
9
|
+
model.find(id).extend(InstanceMethods)
|
10
|
+
rescue
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def first(options = {})
|
15
|
+
model.first(options).extend(InstanceMethods)
|
16
|
+
end
|
17
|
+
|
18
|
+
def all(options = {})
|
19
|
+
model.all(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def all_in(ids, options = {})
|
23
|
+
options[:conditions] = ["id IN (?)", ids]
|
24
|
+
model.all(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def paginated(options = {})
|
28
|
+
page = options.delete(:page) || 1
|
29
|
+
per_page = options.delete(:per_page) || MerbAdmin[:per_page]
|
30
|
+
|
31
|
+
page_count = (count(options).to_f / per_page).ceil
|
32
|
+
|
33
|
+
options.merge!({
|
34
|
+
:limit => per_page,
|
35
|
+
:offset => (page - 1) * per_page
|
36
|
+
})
|
37
|
+
|
38
|
+
[page_count, all(options)]
|
39
|
+
end
|
40
|
+
|
41
|
+
def create(params = {})
|
42
|
+
model.create(params)
|
43
|
+
end
|
44
|
+
|
45
|
+
def new(params = {})
|
46
|
+
model.new(params).extend(InstanceMethods)
|
47
|
+
end
|
48
|
+
|
49
|
+
def destroy_all!
|
50
|
+
model.all.each do |object|
|
51
|
+
object.destroy
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_many_associations
|
56
|
+
associations.select do |association|
|
57
|
+
association[:type] == :has_many
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def has_one_associations
|
62
|
+
associations.select do |association|
|
63
|
+
association[:type] == :has_one
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def belongs_to_associations
|
68
|
+
associations.select do |association|
|
69
|
+
association[:type] == :belongs_to
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def associations
|
74
|
+
model.reflect_on_all_associations.map do |association|
|
75
|
+
{
|
76
|
+
:name => association.name,
|
77
|
+
:pretty_name => association.name.to_s.gsub('_', ' ').capitalize,
|
78
|
+
:type => association.macro,
|
79
|
+
:parent_model => association_parent_model_lookup(association),
|
80
|
+
:parent_key => association_parent_key_lookup(association),
|
81
|
+
:child_model => association_child_model_lookup(association),
|
82
|
+
:child_key => association_child_key_lookup(association),
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def properties
|
88
|
+
model.columns.map do |property|
|
89
|
+
{
|
90
|
+
:name => property.name.to_sym,
|
91
|
+
:pretty_name => property.human_name,
|
92
|
+
:type => property.type,
|
93
|
+
:length => property.limit,
|
94
|
+
:nullable? => property.null,
|
95
|
+
:serial? => property.primary,
|
96
|
+
:key? => property.primary,
|
97
|
+
:flag_map => property.type.respond_to?(:flag_map) ? property.type.flag_map : nil,
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def association_parent_model_lookup(association)
|
105
|
+
case association.macro
|
106
|
+
when :belongs_to
|
107
|
+
association.klass
|
108
|
+
when :has_one, :has_many
|
109
|
+
association.active_record
|
110
|
+
else
|
111
|
+
raise "Unknown association type"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def association_parent_key_lookup(association)
|
116
|
+
[:id]
|
117
|
+
end
|
118
|
+
|
119
|
+
def association_child_model_lookup(association)
|
120
|
+
case association.macro
|
121
|
+
when :belongs_to
|
122
|
+
association.active_record
|
123
|
+
when :has_one, :has_many
|
124
|
+
association.klass
|
125
|
+
else
|
126
|
+
raise "Unknown association type"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def association_child_key_lookup(association)
|
131
|
+
case association.macro
|
132
|
+
when :belongs_to
|
133
|
+
["#{association.class_name.snake_case}_id".to_sym]
|
134
|
+
when :has_one, :has_many
|
135
|
+
[association.primary_key_name.to_sym]
|
136
|
+
else
|
137
|
+
raise "Unknown association type"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
module InstanceMethods
|
142
|
+
def id
|
143
|
+
super
|
144
|
+
end
|
145
|
+
|
146
|
+
def save
|
147
|
+
super
|
148
|
+
end
|
149
|
+
|
150
|
+
def destroy
|
151
|
+
super
|
152
|
+
end
|
153
|
+
|
154
|
+
def update_attributes(attributes)
|
155
|
+
super
|
156
|
+
end
|
157
|
+
|
158
|
+
def errors
|
159
|
+
super
|
160
|
+
end
|
161
|
+
|
162
|
+
def clear_association(association)
|
163
|
+
association.clear
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|