simple_admin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +12 -0
  4. data/README.rdoc +318 -0
  5. data/Rakefile +67 -0
  6. data/TODO.rdoc +4 -0
  7. data/app/assets/images/simple_admin/active_admin/admin_notes_icon.png +0 -0
  8. data/app/assets/images/simple_admin/active_admin/loading.gif +0 -0
  9. data/app/assets/images/simple_admin/active_admin/nested_menu_arrow.gif +0 -0
  10. data/app/assets/images/simple_admin/active_admin/nested_menu_arrow_dark.gif +0 -0
  11. data/app/assets/images/simple_admin/active_admin/orderable.png +0 -0
  12. data/app/assets/javascripts/simple_admin/active_admin.js +434 -0
  13. data/app/assets/stylesheets/simple_admin/active_admin.css +1445 -0
  14. data/app/controllers/simple_admin/admin_controller.rb +117 -0
  15. data/app/helpers/simple_admin/admin_helper.rb +11 -0
  16. data/app/helpers/simple_admin/display_helper.rb +38 -0
  17. data/app/helpers/simple_admin/filter_helper.rb +147 -0
  18. data/app/helpers/simple_admin/header_helper.rb +60 -0
  19. data/app/helpers/simple_admin/path_helper.rb +12 -0
  20. data/app/helpers/simple_admin/sidebar_helper.rb +9 -0
  21. data/app/helpers/simple_admin/table_helper.rb +39 -0
  22. data/app/helpers/simple_admin/title_helper.rb +35 -0
  23. data/app/views/layouts/simple_admin.html.erb +41 -0
  24. data/app/views/simple_admin/admin/_form.html.erb +16 -0
  25. data/app/views/simple_admin/admin/edit.html.erb +7 -0
  26. data/app/views/simple_admin/admin/index.csv.erb +19 -0
  27. data/app/views/simple_admin/admin/index.html.erb +82 -0
  28. data/app/views/simple_admin/admin/new.html.erb +7 -0
  29. data/app/views/simple_admin/admin/show.html.erb +22 -0
  30. data/config/routes.rb +8 -0
  31. data/lib/rails/generators/simple_admin/simple_admin_generator.rb +33 -0
  32. data/lib/rails/generators/simple_admin/templates/initializer.rb +76 -0
  33. data/lib/simple_admin.rb +100 -0
  34. data/lib/simple_admin/attributes.rb +80 -0
  35. data/lib/simple_admin/breadcrumbs.rb +24 -0
  36. data/lib/simple_admin/builder.rb +35 -0
  37. data/lib/simple_admin/engine.rb +8 -0
  38. data/lib/simple_admin/filters.rb +5 -0
  39. data/lib/simple_admin/interface.rb +55 -0
  40. data/lib/simple_admin/section.rb +30 -0
  41. data/lib/simple_admin/version.rb +3 -0
  42. data/rails/init.rb +2 -0
  43. data/simple_admin.gemspec +34 -0
  44. data/spec/acceptance/admin_thing_spec.rb +13 -0
  45. data/spec/controllers/simple_admin/admin_controller_spec.rb +95 -0
  46. data/spec/factories.rb +14 -0
  47. data/spec/simple_admin/attributes_spec.rb +106 -0
  48. data/spec/simple_admin/breadcrumbs_spec.rb +18 -0
  49. data/spec/simple_admin/builder_spec.rb +57 -0
  50. data/spec/simple_admin/engine_spec.rb +9 -0
  51. data/spec/simple_admin/filters_spec.rb +16 -0
  52. data/spec/simple_admin/interface_spec.rb +98 -0
  53. data/spec/simple_admin/section_spec.rb +63 -0
  54. data/spec/simple_admin/simple_admin_spec.rb +68 -0
  55. data/spec/spec_helper.rb +32 -0
  56. metadata +285 -0
@@ -0,0 +1,41 @@
1
+ <!DOCTYPE html><html>
2
+ <head>
3
+ <meta content="text/html; charset=utf-8" http-equiv="Content-type"/>
4
+ <title><%= title %></title>
5
+ <%- if protect_against_forgery? -%>
6
+ <meta name="authenticity-token" id="authenticity-token" content="<%= form_authenticity_token %>" />
7
+ <%- end -%>
8
+ <%= stylesheet_link_tag SimpleAdmin.stylesheet %>
9
+ <%= javascript_include_tag SimpleAdmin.javascript %>
10
+ </head>
11
+ <body class="index admin_occasions">
12
+ <div id="wrapper">
13
+ <div id="header">
14
+ <h1 id="site_title"><%= site_title %></h1>
15
+ <%= tabs %>
16
+ <%= utility_nav %>
17
+ </div>
18
+ <div id="title_bar">
19
+ <%= breadcrumbs %>
20
+ <h2 id="page_title"><%= page_title %></h2>
21
+ <%= action_items %>
22
+ </div>
23
+ <div class="with_sidebar" id="active_admin_content">
24
+ <div id="main_content_wrapper">
25
+ <div id="main_content">
26
+ <%= yield :content %>
27
+ </div>
28
+ </div>
29
+ <div id="sidebar">
30
+ <%= yield :sidebar %>
31
+ </div>
32
+ </div>
33
+ <div id="footer">
34
+ <%= yield :footer %>
35
+ </div>
36
+ </div>
37
+ </body>
38
+ </html>
39
+
40
+
41
+
@@ -0,0 +1,16 @@
1
+ <%= send(SimpleAdmin.form_for_helper, @resource, :as => @interface.member.to_sym,
2
+ :url => resource_path(@resource), :html => {:class => "formtastic #{@interface.member}"}) do |form| %>
3
+ <fieldset class="inputs">
4
+ <ol>
5
+ <% @interface.attributes_for(:form).each do |col| %>
6
+ <%= form.input col.attribute %>
7
+ <% end %>
8
+ </ol>
9
+ </fieldset>
10
+ <fieldset class="buttons">
11
+ <ol>
12
+ <li class="commit"><input class="save" name="commit" type="submit" value="Save <%= @interface.member.titleize %>"></li>
13
+ <li class="cancel"><%= link_to "Cancel", send("simple_admin_#{@interface.collection}_path") %></li>
14
+ </ol>
15
+ </fieldset>
16
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <% content_for :content do %>
2
+ <% render :partial => 'form' %>
3
+ <% end %>
4
+
5
+ <% content_for :sidebar do %>
6
+ <%= sidebars %>
7
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <%-
2
+ # Based on Active Admin
3
+ generator = RUBY_VERSION =~ /^1.8/ ? FasterCSV : CSV
4
+
5
+ output = generator.generate do |csv|
6
+ cols = @interface.attributes_for(:index)
7
+ csv << cols.map(&:title)
8
+ @collection.each do |object|
9
+ csv << cols.map do |col|
10
+ if col.data
11
+ col.data.call(object, col)
12
+ else
13
+ pretty_format(object.send(col.attribute))
14
+ end
15
+ end
16
+ end
17
+ end
18
+ %>
19
+ <%= output.html_safe %>
@@ -0,0 +1,82 @@
1
+ <% content_for :content do %>
2
+ <div class="paginated_collection">
3
+ <div class="pagination_information">
4
+ <% if @collection.num_pages < 2 %>
5
+ <% if @collection.size == 0 %>
6
+ No <%= @interface.collection.titleize %> found
7
+ <% elsif @collection.size == 1 %>
8
+ Displaying <b>1</b> <%= @interface.member.titleize %>
9
+ <% else %>
10
+ Displaying <b>all <%= @collection.size %></b> <%= @interface.collection.titleize %>
11
+ <% end %>
12
+ <% else %>
13
+ Displaying <%= @interface.collection.titleize %>
14
+ <b><%= (@collection.current_page * SimpleAdmin.default_per_page) - SimpleAdmin.default_per_page + 1 %>&nbsp;-&nbsp;<%= (@collection.current_page * SimpleAdmin.default_per_page) > @collection.total_count ? @collection.total_count : (@collection.current_page * SimpleAdmin.default_per_page) %></b>
15
+ of <b><%= @collection.total_count %></b> in total
16
+ <% end %>
17
+ </div>
18
+ <div class="paginated_collection_contents">
19
+ <div class="index_content">
20
+ <div class="index_as_table">
21
+ <table border="0" class="index_table" cellspacing="0" cellpadding="0" id="<%= @interface.collection %>">
22
+ <thead>
23
+ <tr>
24
+ <% @interface.attributes_for(:index).each do |col| %>
25
+ <th class="<%= sortable_header_classes_for(col) %>">
26
+ <%= link_to(col.title,
27
+ request.query_parameters.merge(:order => "#{col.sort_key}_#{order_for_sort_key(col.sort_key)}").except(:page)) %>
28
+ </th>
29
+ <% end %>
30
+ <th>&nbsp;</th>
31
+ </tr>
32
+ </thead>
33
+ <tbody>
34
+ <% @collection.each do |object| %>
35
+ <tr class="<%= cycle('odd', 'even') %>">
36
+ <% @interface.attributes_for(:index).each do |col| %>
37
+ <td>
38
+ <% if col.data %>
39
+ <%= col.data.call(object, col) %>
40
+ <% else %>
41
+ <%= pretty_format(object.send(col.attribute)) %>
42
+ <% end %>
43
+ </td>
44
+ <% end %>
45
+ <td><%= resource_actions(object) %></td>
46
+ </tr>
47
+ <% end %>
48
+ </tbody>
49
+ </table>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ <div id="index_footer">
54
+ Download:&nbsp;
55
+ <% [:csv, :xml, :json].each do |format| %>
56
+ <%= link_to format.to_s.upcase, { :format => format}.merge(request.query_parameters.except(:commit, :format)) %>
57
+ <% end %>
58
+ </div>
59
+ <%= paginate(@collection) %>
60
+ </div>
61
+ <% end %>
62
+
63
+ <% content_for :sidebar do %>
64
+ <div class="panel sidebar_section" id="filters_sidebar_section">
65
+ <h3>Filters</h3>
66
+ <div class="panel_contents">
67
+ <%= form_tag '', :class => "q_search", :id => "q_search", :method => "get" do %>
68
+ <% @interface.filters_for(:index).each do |col| %>
69
+ <%= filter_for(col.attribute, @interface.constant) %>
70
+ <% end %>
71
+ <div class="buttons">
72
+ <input name="commit" type="submit" value="Filter" />
73
+ <a href="#" class="clear_filters_btn">Clear Filters</a>
74
+ <input id="order" name="order" type="hidden" value="id_desc" />
75
+ <input id="scope" name="scope" type="hidden" />
76
+ </div>
77
+ <% end %>
78
+ </div>
79
+ </div>
80
+
81
+ <%= sidebars %>
82
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <% content_for :content do %>
2
+ <% render :partial => 'form' %>
3
+ <% end %>
4
+
5
+ <% content_for :sidebar do %>
6
+ <%= sidebars %>
7
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <% content_for :content do %>
2
+ <div class="panel">
3
+ <h3><%= @interface.member.titleize %> Details</h3>
4
+ <div class="panel_contents">
5
+ <div class="<%= @interface.member %> attributes_table"
6
+ id="<%= ['attributes_table', @interface.member, @resource.to_param].join("_") %>">
7
+ <table border="0" cellspacing="0" cellpadding="0">
8
+ <% @interface.attributes_for(:show).each do |attr| %>
9
+ <tr>
10
+ <th><%= attr.title %></th>
11
+ <td><%= data_for(attr) %></td>
12
+ </tr>
13
+ <% end %>
14
+ </table>
15
+ </div>
16
+ </div>
17
+ </div>
18
+ <% end %>
19
+
20
+ <% content_for :sidebar do %>
21
+ <%= sidebars %>
22
+ <% end %>
@@ -0,0 +1,8 @@
1
+ SimpleAdmin::Engine.routes.draw do
2
+ SimpleAdmin::registered.each do |interface|
3
+ resources interface.collection,
4
+ :controller => :admin,
5
+ :as => "simple_admin_#{interface.collection}",
6
+ :defaults => {:interface => interface.collection}
7
+ end
8
+ end
@@ -0,0 +1,33 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class SimpleAdminGenerator < Rails::Generators::Base
5
+ desc <<-CONTENT
6
+ This will mount SimpleAdmin in your routes.rb using the admin \
7
+ path. It will also install a simple_admin initializer file in \
8
+ your config where you can setup the options.\
9
+ \
10
+ Once complete, register admin interfaces like app/admin/posts.rb:\
11
+ \
12
+ SimpleAdmin.register :posts do\
13
+ end\
14
+
15
+ CONTENT
16
+
17
+ def self.source_root
18
+ File.join(File.dirname(__FILE__), 'templates')
19
+ end
20
+
21
+ def copy_initializer_file
22
+ copy_file 'initializer.rb', 'config/initializers/simple_admin.rb'
23
+ end
24
+
25
+ def app_admin
26
+ empty_directory('app/admin')
27
+ end
28
+
29
+ def add_simple_admin_routes
30
+ simple_admin_routes = %(mount SimpleAdmin::Engine => '/admin'\n)
31
+ route simple_admin_routes
32
+ end
33
+ end
@@ -0,0 +1,76 @@
1
+ # You can use this file to setup all of your simple_admin
2
+ # configuration defaults. If you change this file, you may
3
+ # need to restart your Rails environment
4
+
5
+ SimpleAdmin.setup do |config|
6
+
7
+ # The +require_user_method+ setting allows you to specify which
8
+ # controller method should be called before rendering any admin
9
+ # pages. There is no default for this method. If it is not
10
+ # present then simple_admin will not do any admin verification
11
+ # prior to performing its controller actions.
12
+ #
13
+ # It is possible to check for admin status using application level
14
+ # before filters or even using the +before+ block within each
15
+ # individual admin interface registration.
16
+ #
17
+ # To set this, specify the controller method you want called as a
18
+ # symbol:
19
+ #
20
+ # config.require_user_method = :require_admin
21
+
22
+
23
+ # The +current_user_method+ is used by simple_admin to determine if
24
+ # there is a logged in user. There is no default for this property.
25
+ # If it is not present, no logged in user method will be checked and
26
+ # user information will not be displayed in the header of admin forms.
27
+ #
28
+ # To set this, specify the controller method you want called as a
29
+ # symbol:
30
+ #
31
+ # config.current_user_method = :current_user
32
+
33
+
34
+ # The +current_user_name_method+ is used by simple_admin to access the
35
+ # display name of the currently logged in user. There is no default for
36
+ # this property. If it is not present, any logged in user name will
37
+ # not be displayed in the header of admin forms.
38
+ #
39
+ # To set this, specify the controller method you want called as a
40
+ # symbol:
41
+ #
42
+ # config.current_user_name_method = :current_user_name
43
+
44
+
45
+ # The +site_title+ is displayed in the header of admin pages. If empty
46
+ # this value defaults to +Rails.application.class.parent_name.titleize+:
47
+ #
48
+ # config.site_title = "My Website"
49
+
50
+
51
+ # The +default_per_page+ value allows you to control how many results
52
+ # appear on admin index pages. This value is fed into kaminari and is
53
+ # defaulted to 25 if not present:
54
+ #
55
+ # config.default_per_page = 25
56
+
57
+
58
+ # The +form_for_helper+ defaults to the +:semantic_form_for+ helper
59
+ # from formtastic. If you want to use another engine, like +simple_form+
60
+ # you can change this property and the gem requirements. Note that the
61
+ # default styling from active_admin relies on formtastic output.
62
+ #
63
+ # config.form_for_helper = :semantic_form_for
64
+
65
+
66
+ # The +stylesheet+ and +javascript+ properties are used within the admin
67
+ # layout and are defaulted to the active_admin styles and scripts. You
68
+ # may want to override these styles, or simply move them to your public
69
+ # folder so that they are not served through rails. These properties allow
70
+ # you to manage where the assets are stored (either within the default
71
+ # pipeline or no):
72
+ #
73
+ # config.stylesheet = "simple_admin/active_admin.css"
74
+ # config.javascript = "simple_admin/active_admin.js"
75
+
76
+ end
@@ -0,0 +1,100 @@
1
+ module SimpleAdmin
2
+ require 'simple_admin/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
3
+
4
+ mattr_accessor :require_user_method,
5
+ :current_user_method,
6
+ :current_user_name_method,
7
+ :site_title,
8
+ :default_per_page,
9
+ :form_for_helper,
10
+ :stylesheet,
11
+ :javascript
12
+
13
+ class << self
14
+ def site_title
15
+ @@site_title || Rails.application.class.parent_name.titleize
16
+ end
17
+
18
+ def default_per_page
19
+ @@default_per_page || 25
20
+ end
21
+
22
+ def form_for_helper
23
+ @@form_for_helper || :semantic_form_for
24
+ end
25
+
26
+ def stylesheet
27
+ @@stylesheet || "simple_admin/active_admin.css"
28
+ end
29
+
30
+ def javascript
31
+ @@javascript || "simple_admin/active_admin.js"
32
+ end
33
+
34
+ def registered
35
+ unless defined?(@@registered) && @@registered
36
+ @@registered = []
37
+ # We load up all of the admin files on launch, if they change you need to restart
38
+ begin
39
+ Dir[Rails.root.join("app/admin/**/*.rb")].each {|f| require f}
40
+ rescue LoadError => e
41
+ # For certain kinds of load errors, we want to give a more helpful message
42
+ if e.message.match(/Expected .* to define .*/)
43
+ raise(InvalidAdminFile.new(e))
44
+ else
45
+ raise e
46
+ end
47
+ end
48
+ end
49
+ @@registered
50
+ end
51
+
52
+ # Called by the initializer
53
+ #
54
+ # SimpleAdmin.setup do |config|
55
+ # config.site_title = "My Website"
56
+ # end
57
+ #
58
+ def setup
59
+ yield self
60
+ end
61
+
62
+ # Registers a model for administration:
63
+ #
64
+ # SimpleAdmin.register :post do
65
+ # end
66
+ #
67
+ # Various configuration options are available within the block
68
+ def register(resource, options={}, &block)
69
+ interface = SimpleAdmin::Interface.new(resource, options, &block)
70
+ self.registered << interface
71
+ interface
72
+ end
73
+ end
74
+
75
+ class ConfigurationNotFound < StandardError
76
+ attr_accessor :message
77
+
78
+ def initialize(option)
79
+ @message = "SimpleAdmin configuration option #{option} not found. " +
80
+ "Please set this in config/initializers/simple_admin.rb."
81
+ end
82
+ end
83
+
84
+ class InvalidAdminFile < LoadError
85
+ attr_accessor :message
86
+
87
+ def initialize(e)
88
+ @message = "#{e.message}. Try renaming app/admin/#{File.basename(e.blamed_files.first)} " +
89
+ "to app/admin/#{File.basename(e.blamed_files.first, '.rb')}s.rb"
90
+ end
91
+ end
92
+
93
+ class UnknownAdminInterface < StandardError
94
+ attr_accessor :message
95
+
96
+ def initialize(e)
97
+ @message = "SimpleAdmin interface unknown, make sure you mount SimpleAdmin in your routes and that you have registered an interface for this resource"
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,80 @@
1
+ module SimpleAdmin
2
+ class Attributes
3
+ # Which columns to skip when automatically rendering a form without any fields specified
4
+ SKIPPED_COLUMNS = [:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
5
+
6
+ attr_reader :attributes
7
+
8
+ # Allows you to specify a set of attributes for a section
9
+ #
10
+ # Available options:
11
+ #
12
+ # :except An array of excluded attribute names as symbols
13
+ # :only An array of attribute names for this view
14
+ #
15
+ def initialize(interface, section, options={}, &block)
16
+ @interface = interface
17
+ @section = section
18
+ defaults(options)
19
+ instance_eval(&block) if block_given?
20
+ end
21
+
22
+ # Clear the attributes for this section
23
+ def clear
24
+ @attributes = []
25
+ end
26
+
27
+ # Define or override an attribute for this section
28
+ #
29
+ # Available options:
30
+ #
31
+ # :sortable +true+ or +false+ (defaults to +true+)
32
+ # :sort_key a column name used when sorting this column (defaults to the column for this attribute)
33
+ #
34
+ def attribute(name, options={}, &block)
35
+ attr = @attributes.find {|attr| attr.attribute == name.to_sym }
36
+ unless attr
37
+ attr = OpenStruct.new
38
+ @attributes << attr
39
+ end
40
+ attr.attribute = name.to_sym
41
+ attr.options = options
42
+ attr.data = block
43
+ attr.title = options[:title] || name.to_s.titleize
44
+ attr.sortable = options[:sortable].nil? || !(options[:sortable] === false)
45
+ attr.sort_key = (options[:sort_key] || name).to_s
46
+ attr
47
+ end
48
+
49
+ # Define the default attributes for this section
50
+ #
51
+ # If the current section is a form it will only use content columns and will
52
+ # skip timestamps and primary key fields.
53
+ #
54
+ # Available options:
55
+ #
56
+ # :except An array of excluded attribute names as symbols
57
+ # :only An array of attribute names for this view
58
+ #
59
+ def defaults(options={})
60
+ clear
61
+ if @section.section == :form
62
+ association_columns = @interface.constant.reflections.map do |name, ref|
63
+ if ref.macro == :belongs_to && ref.options[:polymorphic] != true
64
+ name.to_sym
65
+ end
66
+ end.compact
67
+
68
+ cols = @interface.constant.content_columns.map {|col| col.name.to_sym }
69
+ cols += association_columns
70
+ cols -= SKIPPED_COLUMNS
71
+ cols.compact!
72
+ else
73
+ cols = @interface.constant.columns.map{|col| col.name.to_sym }
74
+ end
75
+ cols -= options[:except] if options[:except]
76
+ cols &= options[:only] if options[:only]
77
+ cols.each {|col| attribute(col.to_s) }
78
+ end
79
+ end
80
+ end