simple_admin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,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,8 @@
1
+ require 'simple_admin'
2
+ require 'rails'
3
+
4
+ module SimpleAdmin
5
+ class Engine < Rails::Engine
6
+ isolate_namespace SimpleAdmin
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module SimpleAdmin
2
+ class Filters < Attributes
3
+ alias_method :filter, :attribute
4
+ end
5
+ 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
@@ -0,0 +1,3 @@
1
+ module SimpleAdmin
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,2 @@
1
+ # This is here so that this can be treated like a plugin instead of a gem
2
+ require 'simple_admin'
@@ -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,13 @@
1
+ require 'spec_helper'
2
+
3
+ feature "Things admin" do
4
+ background do
5
+ # mocha_teardown
6
+ end
7
+
8
+ scenario "Viewing a list of things" do
9
+ visit "/things"
10
+ page.should have_content("things")
11
+ # flunk
12
+ end
13
+ 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
@@ -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
+