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