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.
- 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
|
+
|