simple_admin 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +12 -0
- data/README.rdoc +318 -0
- data/Rakefile +67 -0
- data/TODO.rdoc +4 -0
- data/app/assets/images/simple_admin/active_admin/admin_notes_icon.png +0 -0
- data/app/assets/images/simple_admin/active_admin/loading.gif +0 -0
- data/app/assets/images/simple_admin/active_admin/nested_menu_arrow.gif +0 -0
- data/app/assets/images/simple_admin/active_admin/nested_menu_arrow_dark.gif +0 -0
- data/app/assets/images/simple_admin/active_admin/orderable.png +0 -0
- data/app/assets/javascripts/simple_admin/active_admin.js +434 -0
- data/app/assets/stylesheets/simple_admin/active_admin.css +1445 -0
- data/app/controllers/simple_admin/admin_controller.rb +117 -0
- data/app/helpers/simple_admin/admin_helper.rb +11 -0
- data/app/helpers/simple_admin/display_helper.rb +38 -0
- data/app/helpers/simple_admin/filter_helper.rb +147 -0
- data/app/helpers/simple_admin/header_helper.rb +60 -0
- data/app/helpers/simple_admin/path_helper.rb +12 -0
- data/app/helpers/simple_admin/sidebar_helper.rb +9 -0
- data/app/helpers/simple_admin/table_helper.rb +39 -0
- data/app/helpers/simple_admin/title_helper.rb +35 -0
- data/app/views/layouts/simple_admin.html.erb +41 -0
- data/app/views/simple_admin/admin/_form.html.erb +16 -0
- data/app/views/simple_admin/admin/edit.html.erb +7 -0
- data/app/views/simple_admin/admin/index.csv.erb +19 -0
- data/app/views/simple_admin/admin/index.html.erb +82 -0
- data/app/views/simple_admin/admin/new.html.erb +7 -0
- data/app/views/simple_admin/admin/show.html.erb +22 -0
- data/config/routes.rb +8 -0
- data/lib/rails/generators/simple_admin/simple_admin_generator.rb +33 -0
- data/lib/rails/generators/simple_admin/templates/initializer.rb +76 -0
- data/lib/simple_admin.rb +100 -0
- data/lib/simple_admin/attributes.rb +80 -0
- data/lib/simple_admin/breadcrumbs.rb +24 -0
- data/lib/simple_admin/builder.rb +35 -0
- data/lib/simple_admin/engine.rb +8 -0
- data/lib/simple_admin/filters.rb +5 -0
- data/lib/simple_admin/interface.rb +55 -0
- data/lib/simple_admin/section.rb +30 -0
- data/lib/simple_admin/version.rb +3 -0
- data/rails/init.rb +2 -0
- data/simple_admin.gemspec +34 -0
- data/spec/acceptance/admin_thing_spec.rb +13 -0
- data/spec/controllers/simple_admin/admin_controller_spec.rb +95 -0
- data/spec/factories.rb +14 -0
- data/spec/simple_admin/attributes_spec.rb +106 -0
- data/spec/simple_admin/breadcrumbs_spec.rb +18 -0
- data/spec/simple_admin/builder_spec.rb +57 -0
- data/spec/simple_admin/engine_spec.rb +9 -0
- data/spec/simple_admin/filters_spec.rb +16 -0
- data/spec/simple_admin/interface_spec.rb +98 -0
- data/spec/simple_admin/section_spec.rb +63 -0
- data/spec/simple_admin/simple_admin_spec.rb +68 -0
- data/spec/spec_helper.rb +32 -0
- metadata +285 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module SimpleAdmin
|
2
|
+
class Breadcrumbs
|
3
|
+
# Returns an array of links to use in a breadcrumb
|
4
|
+
def self.parse(path, action)
|
5
|
+
parts = path.gsub(/^\//, '').split('/')
|
6
|
+
parts.pop unless %w{ create update }.include?(action)
|
7
|
+
crumbs = []
|
8
|
+
parts.each_with_index do |part, index|
|
9
|
+
name = ""
|
10
|
+
if part =~ /^\d/ && parent = parts[index - 1]
|
11
|
+
begin
|
12
|
+
parent_class = parent.singularize.camelcase.constantize
|
13
|
+
obj = parent_class.find(part.to_i)
|
14
|
+
name = obj.display_name if obj.respond_to?(:display_name)
|
15
|
+
rescue
|
16
|
+
end
|
17
|
+
end
|
18
|
+
name = part.titlecase if name == ""
|
19
|
+
crumbs << [name, "/" + parts[0..index].join('/')]
|
20
|
+
end
|
21
|
+
crumbs
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module SimpleAdmin
|
2
|
+
class Builder
|
3
|
+
attr_accessor :interface
|
4
|
+
|
5
|
+
def initialize(interface, &block)
|
6
|
+
@interface = interface
|
7
|
+
instance_eval(&block) if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def section(sym, options={}, &block)
|
11
|
+
@interface.sections[sym] = SimpleAdmin::Section.new(@interface, sym, options, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def index(options={}, &block)
|
15
|
+
section(:index, options, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def form(options={}, &block)
|
19
|
+
section(:form, options, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def show(options={}, &block)
|
23
|
+
section(:show, options, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def before(options={}, &block)
|
27
|
+
options[:data] = block
|
28
|
+
options[:actions] = options[:actions] || [:index, :show, :edit, :new, :delete, :create, :update]
|
29
|
+
options[:actions] -= options[:except] if options[:except]
|
30
|
+
options[:actions] &= options[:only] if options[:only]
|
31
|
+
@interface.before << options
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module SimpleAdmin
|
4
|
+
class Interface
|
5
|
+
attr_reader :collection, :member, :constant, :options, :before, :sections
|
6
|
+
|
7
|
+
def initialize(resource, options={}, &block)
|
8
|
+
if resource.is_a?(Class)
|
9
|
+
@constant = resource
|
10
|
+
@symbol = resource.name.downcase.to_sym
|
11
|
+
else
|
12
|
+
@constant = resource.to_s.camelize.singularize.constantize
|
13
|
+
@symbol = resource.to_sym
|
14
|
+
end
|
15
|
+
@member = @symbol.to_s.singularize
|
16
|
+
@collection = @symbol.to_s.pluralize
|
17
|
+
@options = options
|
18
|
+
@sections = {}
|
19
|
+
@before = []
|
20
|
+
@builder = SimpleAdmin::Builder.new(self, &block)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def filters_for(sym)
|
25
|
+
section(sym).options[:filters].attributes
|
26
|
+
end
|
27
|
+
|
28
|
+
def attributes_for(sym)
|
29
|
+
section(sym).options[:attributes].attributes
|
30
|
+
end
|
31
|
+
|
32
|
+
def sidebars_for(sym)
|
33
|
+
section(sym).options[:sidebars] || []
|
34
|
+
end
|
35
|
+
|
36
|
+
def options_for(sym)
|
37
|
+
section(sym).options
|
38
|
+
end
|
39
|
+
|
40
|
+
def actions
|
41
|
+
arr = @options[:actions] || [:index, :show, :edit, :new, :delete, :create, :update]
|
42
|
+
arr -= @options[:except] if @options[:except]
|
43
|
+
arr &= @options[:only] if @options[:only]
|
44
|
+
arr
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def section(sym)
|
50
|
+
@sections[sym] ||= SimpleAdmin::Section.new(self, sym)
|
51
|
+
@sections[sym]
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SimpleAdmin
|
2
|
+
class Section
|
3
|
+
attr_accessor :options, :section
|
4
|
+
|
5
|
+
def initialize(interface, section, options={}, &block)
|
6
|
+
@interface = interface
|
7
|
+
@section = section
|
8
|
+
@options = options
|
9
|
+
@allow_filters = (section == :index)
|
10
|
+
attributes
|
11
|
+
filters if @allow_filters
|
12
|
+
instance_eval(&block) if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def attributes(attr_options={}, &block)
|
16
|
+
self.options[:attributes] = SimpleAdmin::Attributes.new(@interface, self, attr_options, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def filters(filter_options={}, &block)
|
20
|
+
raise "Filters cannot be specified for this section" unless @allow_filters
|
21
|
+
self.options[:filters] = SimpleAdmin::Filters.new(@interface, self, filter_options, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def sidebar(sidebar_options={}, &block)
|
25
|
+
sidebar_options[:data] = block
|
26
|
+
self.options[:sidebars] ||= []
|
27
|
+
self.options[:sidebars] << sidebar_options
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "simple_admin/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "simple_admin"
|
7
|
+
s.version = SimpleAdmin::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jeff Rafter"]
|
10
|
+
s.email = ["jeffrafter@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/jeffrafter/simple_admin"
|
12
|
+
s.summary = %q{Simple administrative interfaces for data in your rails application}
|
13
|
+
s.description = %q{Use SimpleAdmin to build out filtering, searchable, editable interfaces for your models}
|
14
|
+
|
15
|
+
s.rubyforge_project = "simple_admin"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# Formtastic is a dependency if you are using the default form helper
|
23
|
+
s.add_runtime_dependency("rails", [">= 3.1.0rc4"])
|
24
|
+
s.add_runtime_dependency("kaminari", [">= 0"])
|
25
|
+
s.add_runtime_dependency("meta_search", [">= 1.1.0.pre"])
|
26
|
+
|
27
|
+
s.add_development_dependency("rspec", [">= 2.6.0"])
|
28
|
+
s.add_development_dependency("rspec-rails", [">= 2.6.0"])
|
29
|
+
s.add_development_dependency("shoulda", [">= 0"])
|
30
|
+
s.add_development_dependency("factory_girl", [">= 0"])
|
31
|
+
s.add_development_dependency("mocha", ["> 0"])
|
32
|
+
s.add_development_dependency("capybara", [">= 0.4.0"])
|
33
|
+
s.add_development_dependency("sqlite3-ruby")
|
34
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SimpleAdmin::AdminController do
|
4
|
+
render_views
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@thing = Factory(:thing)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "index" do
|
11
|
+
it "should render the template" do
|
12
|
+
get :index, :interface => "things"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should render the sidebars" do
|
16
|
+
SimpleAdmin.expects(:success)
|
17
|
+
@block = Proc.new {|sidebar| SimpleAdmin.success if sidebar[:title] == 'Awes!' }
|
18
|
+
@sidebar = {:title => 'Awes!', :data => @block}
|
19
|
+
SimpleAdmin::Interface.any_instance.expects(:sidebars_for).with(:index).returns([@sidebar])
|
20
|
+
get :index, :interface => "things"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should render a csv" do
|
24
|
+
get :index, :interface => "things", :format => "csv"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "new" do
|
29
|
+
it "should render the template" do
|
30
|
+
get :new, :interface => "things"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "show" do
|
35
|
+
it "should render the template" do
|
36
|
+
get :show, :id => @thing.id, :interface => "things"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "edit" do
|
41
|
+
it "should render the template" do
|
42
|
+
get :edit, :id => @thing.id, :interface => "things"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "create" do
|
47
|
+
before :each do
|
48
|
+
@params = {:interface => "things", :thing => Factory.attributes_for(:thing)}
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should create a new thing" do
|
52
|
+
expect {
|
53
|
+
post :create, @params
|
54
|
+
}.to change(Thing, :count)
|
55
|
+
|
56
|
+
Thing.last.name.should == @params[:thing][:name]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should redirect to the collection page" do
|
60
|
+
post :create, @params
|
61
|
+
|
62
|
+
response.should be_redirect
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "update" do
|
67
|
+
before :each do
|
68
|
+
@params = {:id => @thing.id, :interface => "things", :thing => Factory.attributes_for(:thing)}
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should update a thing" do
|
72
|
+
expect {
|
73
|
+
post :update, @params
|
74
|
+
}.to_not change(Thing, :count)
|
75
|
+
|
76
|
+
@thing.reload
|
77
|
+
@thing.name.should == @params[:thing][:name]
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should redirect to the collection page" do
|
81
|
+
post :update, @params
|
82
|
+
|
83
|
+
response.should be_redirect
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "destroy" do
|
88
|
+
it "should destroy a thing" do
|
89
|
+
expect {
|
90
|
+
post :destroy, :id => @thing.id, :interface => "things"
|
91
|
+
}.to change(Thing, :count)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
data/spec/factories.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Factory.sequence :name_sequence do |n|
|
2
|
+
"name #{n}"
|
3
|
+
end
|
4
|
+
|
5
|
+
Factory.define :place do |f|
|
6
|
+
f.name { Factory.next :name_sequence }
|
7
|
+
end
|
8
|
+
|
9
|
+
Factory.define :thing do |f|
|
10
|
+
f.name { Factory.next :name_sequence }
|
11
|
+
f.happy true
|
12
|
+
f.age 10
|
13
|
+
f.association :place
|
14
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SimpleAdmin::Attributes do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@interface = SimpleAdmin.register(:thing)
|
7
|
+
@section = @interface.send(:section, :index)
|
8
|
+
@attributes = SimpleAdmin::Attributes.new(@interface, @section)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "initialize" do
|
12
|
+
it "defaults the attributes on initialize" do
|
13
|
+
@attributes.attributes.should_not be_blank
|
14
|
+
end
|
15
|
+
|
16
|
+
it "executes the block if submitted" do
|
17
|
+
@attributes = SimpleAdmin::Attributes.new(@interface, @section) do
|
18
|
+
clear
|
19
|
+
end
|
20
|
+
@attributes.attributes.should be_blank
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "clear" do
|
25
|
+
it "clears the list of attributes" do
|
26
|
+
@attributes.clear
|
27
|
+
@attributes.attributes.should be_blank
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "defaults" do
|
32
|
+
it "creates a default set of attributes based on the columns" do
|
33
|
+
@attributes.attributes.map(&:attribute).should == [:id, :name, :happy, :age, :place_id, :created_at, :updated_at]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "rejects attributes that are listed in the except option" do
|
37
|
+
@attributes = SimpleAdmin::Attributes.new(@interface, @section, :except => [:created_at, :updated_at])
|
38
|
+
@attributes.attributes.map(&:attribute).should == [:id, :name, :happy, :age, :place_id]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "only includes attributes that are listed in the only option" do
|
42
|
+
@attributes = SimpleAdmin::Attributes.new(@interface, @section, :only => [:name, :happy, :age])
|
43
|
+
@attributes.attributes.map(&:attribute).should == [:name, :happy, :age]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "only includes content attributes if the attributes are on a form section" do
|
47
|
+
@section = @interface.send(:section, :form)
|
48
|
+
@attributes = SimpleAdmin::Attributes.new(@interface, @section)
|
49
|
+
@attributes.attributes.map(&:attribute).should_not include(:id)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "excludes skipped attributes if the attributes are on a form section" do
|
53
|
+
@section = @interface.send(:section, :form)
|
54
|
+
@attributes = SimpleAdmin::Attributes.new(@interface, @section)
|
55
|
+
@attributes.attributes.map(&:attribute).should_not include(:created_at)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "attribute" do
|
60
|
+
before :each do
|
61
|
+
@attributes.clear
|
62
|
+
end
|
63
|
+
|
64
|
+
it "adds an attribute" do
|
65
|
+
@attributes.attribute(:name)
|
66
|
+
@attributes.attributes.map(&:attribute).should == [:name]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "sets the title" do
|
70
|
+
@attributes.attribute(:name)
|
71
|
+
@attributes.attribute(:age, :title => "Awesome age!")
|
72
|
+
@attributes.attributes.map(&:title).should == ["Name", "Awesome age!"]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "defaults to being sortable" do
|
76
|
+
@attributes.attribute(:name)
|
77
|
+
@attributes.attributes.map(&:sortable).should == [true]
|
78
|
+
end
|
79
|
+
|
80
|
+
it "allows you to mark an attribute as unsortable" do
|
81
|
+
@attributes.attribute(:age, :sortable => false)
|
82
|
+
@attributes.attribute(:happy, :sortable => true)
|
83
|
+
@attributes.attributes.map(&:sortable).should == [false, true]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "allows an alternate sort key" do
|
87
|
+
@attributes.attribute(:name)
|
88
|
+
@attributes.attribute(:happy, :sort_key => :name)
|
89
|
+
@attributes.attributes.map(&:sort_key).should == ["name", "name"]
|
90
|
+
end
|
91
|
+
|
92
|
+
it "accepts a block" do
|
93
|
+
@attributes.attribute(:name) do |object|
|
94
|
+
object
|
95
|
+
end
|
96
|
+
@attributes.attributes.last.data.call(true).should == true
|
97
|
+
end
|
98
|
+
|
99
|
+
it "overrides previously defined attributes" do
|
100
|
+
@attributes.attribute(:name)
|
101
|
+
@attributes.attribute(:name, :title => "Your Name Here")
|
102
|
+
@attributes.attributes.map(&:title).should == ["Your Name Here"]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SimpleAdmin::Breadcrumbs do
|
4
|
+
it "parses the path" do
|
5
|
+
SimpleAdmin::Breadcrumbs.parse("/admin/things", "index").should == [["Admin", "/admin"]]
|
6
|
+
end
|
7
|
+
|
8
|
+
it "includes the last step if creating" do
|
9
|
+
SimpleAdmin::Breadcrumbs.parse("/admin/things", "create").should == [["Admin", "/admin"], ["Things", "/admin/things"]]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "finds the object and uses the display name" do
|
13
|
+
@thing = Factory(:thing)
|
14
|
+
Thing.any_instance.expects(:display_name).returns("A Thing!")
|
15
|
+
SimpleAdmin::Breadcrumbs.parse("/things/#{@thing.id}", "create").should == [["Things", "/things"],["A Thing!", "/things/#{@thing.id}"]]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|