godmin 0.10.3 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +8 -0
- data/README.md +146 -81
- data/app/assets/javascripts/godmin/batch-actions.js +18 -13
- data/app/assets/stylesheets/godmin/index.css.scss +15 -8
- data/app/views/godmin/resource/_batch_actions.html.erb +2 -4
- data/app/views/godmin/resource/_breadcrumb.html.erb +0 -3
- data/app/views/godmin/resource/_button_actions.html.erb +2 -2
- data/app/views/godmin/resource/_filters.html.erb +17 -18
- data/app/views/godmin/resource/_form.html.erb +1 -1
- data/app/views/godmin/resource/_pagination.html.erb +11 -11
- data/app/views/godmin/resource/_scopes.html.erb +4 -4
- data/app/views/godmin/resource/_table.html.erb +30 -30
- data/app/views/godmin/resource/index.html.erb +3 -10
- data/godmin.gemspec +4 -1
- data/lib/generators/godmin/authentication/templates/sessions_controller.rb +1 -1
- data/lib/generators/godmin/install/install_generator.rb +1 -1
- data/lib/generators/godmin/resource/resource_generator.rb +4 -0
- data/lib/generators/godmin/resource/templates/resource_controller.rb +1 -9
- data/lib/generators/godmin/resource/templates/resource_service.rb +8 -0
- data/lib/godmin.rb +4 -2
- data/lib/godmin/{application.rb → application_controller.rb} +1 -1
- data/lib/godmin/authentication.rb +1 -1
- data/lib/godmin/authentication/{sessions.rb → sessions_controller.rb} +1 -1
- data/lib/godmin/authorization.rb +1 -1
- data/lib/godmin/authorization/policy_finder.rb +3 -1
- data/lib/godmin/helpers/application.rb +10 -0
- data/lib/godmin/helpers/batch_actions.rb +7 -4
- data/lib/godmin/helpers/filters.rb +72 -73
- data/lib/godmin/helpers/tables.rb +1 -3
- data/lib/godmin/paginator.rb +47 -0
- data/lib/godmin/rails.rb +1 -6
- data/lib/godmin/resolver.rb +7 -2
- data/lib/godmin/resources/resource_controller.rb +170 -0
- data/lib/godmin/resources/resource_service.rb +82 -0
- data/lib/godmin/resources/resource_service/batch_actions.rb +38 -0
- data/lib/godmin/resources/resource_service/filters.rb +37 -0
- data/lib/godmin/resources/resource_service/ordering.rb +27 -0
- data/lib/godmin/resources/resource_service/pagination.rb +22 -0
- data/lib/godmin/resources/resource_service/scopes.rb +61 -0
- data/lib/godmin/version.rb +1 -1
- data/test/dummy/config/environments/production.rb +1 -1
- data/test/dummy/config/environments/test.rb +1 -1
- data/test/dummy/db/schema.rb +16 -0
- data/test/lib/godmin/helpers/filters_test.rb +26 -0
- data/test/lib/godmin/paginator_test.rb +84 -0
- data/test/lib/godmin/policy_finder_test.rb +35 -6
- data/test/lib/godmin/resolver_test.rb +6 -0
- data/test/lib/godmin/resources/resource_service/batch_actions_test.rb +45 -0
- data/test/lib/godmin/resources/resource_service/filters_test.rb +32 -0
- data/test/lib/godmin/resources/resource_service/ordering_test.rb +37 -0
- data/test/lib/godmin/resources/resource_service/pagination_test.rb +31 -0
- data/test/lib/godmin/resources/resource_service/scopes_test.rb +57 -0
- data/test/lib/godmin/resources/resource_service_test.rb +21 -0
- data/test/test_helper.rb +62 -0
- metadata +75 -17
- data/.hound.yml +0 -3
- data/lib/godmin/resource.rb +0 -177
- data/lib/godmin/resource/batch_actions.rb +0 -45
- data/lib/godmin/resource/filters.rb +0 -41
- data/lib/godmin/resource/ordering.rb +0 -25
- data/lib/godmin/resource/pagination.rb +0 -64
- data/lib/godmin/resource/scopes.rb +0 -54
- data/test/dummy/db/test.sqlite3 +0 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require "godmin/resources/resource_service/batch_actions"
|
2
|
+
require "godmin/resources/resource_service/filters"
|
3
|
+
require "godmin/resources/resource_service/ordering"
|
4
|
+
require "godmin/resources/resource_service/pagination"
|
5
|
+
require "godmin/resources/resource_service/scopes"
|
6
|
+
|
7
|
+
module Godmin
|
8
|
+
module Resources
|
9
|
+
module ResourceService
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
include BatchActions
|
13
|
+
include Filters
|
14
|
+
include Ordering
|
15
|
+
include Pagination
|
16
|
+
include Scopes
|
17
|
+
|
18
|
+
attr_reader :options
|
19
|
+
|
20
|
+
def initialize(options = {})
|
21
|
+
@options = options
|
22
|
+
end
|
23
|
+
|
24
|
+
# TODO: should this raise its own error?
|
25
|
+
def resource_class
|
26
|
+
@options[:resource_class] || self.class.name.demodulize.chomp("Service").constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
def resources_relation
|
30
|
+
resource_class.all
|
31
|
+
end
|
32
|
+
|
33
|
+
def resources(params)
|
34
|
+
apply_pagination(params[:page],
|
35
|
+
apply_order(params[:order],
|
36
|
+
apply_filters(params[:filter],
|
37
|
+
apply_scope(params[:scope],
|
38
|
+
resources_relation
|
39
|
+
)
|
40
|
+
)
|
41
|
+
)
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_resource(id)
|
46
|
+
resources_relation.find(id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_resource(params)
|
50
|
+
resources_relation.new(params)
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_resource(resource)
|
54
|
+
resource.save
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_resource(resource, params)
|
58
|
+
resource.update(params)
|
59
|
+
end
|
60
|
+
|
61
|
+
def attrs_for_index
|
62
|
+
self.class.attrs_for_index
|
63
|
+
end
|
64
|
+
|
65
|
+
def attrs_for_form
|
66
|
+
self.class.attrs_for_form
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
def attrs_for_index(*attrs)
|
71
|
+
@attrs_for_index = attrs if attrs.present?
|
72
|
+
@attrs_for_index || []
|
73
|
+
end
|
74
|
+
|
75
|
+
def attrs_for_form(*attrs)
|
76
|
+
@attrs_for_form = attrs if attrs.present?
|
77
|
+
@attrs_for_form || []
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Godmin
|
2
|
+
module Resources
|
3
|
+
module ResourceService
|
4
|
+
module BatchActions
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
delegate :batch_action_map, to: "self.class"
|
8
|
+
|
9
|
+
def batch_action(action, item_ids)
|
10
|
+
if batch_action?(action)
|
11
|
+
send("batch_action_#{action}", resource_class.find(item_ids))
|
12
|
+
true
|
13
|
+
else
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def batch_action?(action)
|
19
|
+
batch_action_map.key?(action.to_sym)
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def batch_action_map
|
24
|
+
@batch_action_map ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def batch_action(attr, options = {})
|
28
|
+
batch_action_map[attr] = {
|
29
|
+
only: nil,
|
30
|
+
except: nil,
|
31
|
+
confirm: false
|
32
|
+
}.merge(options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Godmin
|
2
|
+
module Resources
|
3
|
+
module ResourceService
|
4
|
+
module Filters
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
delegate :filter_map, to: "self.class"
|
8
|
+
|
9
|
+
def apply_filters(filter_params, resources)
|
10
|
+
if filter_params.present?
|
11
|
+
filter_params.each do |name, value|
|
12
|
+
if filter_map.key?(name.to_sym) && value.present?
|
13
|
+
resources = send("filter_#{name}", resources, value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
resources
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def filter_map
|
22
|
+
@filter_map ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def filter(attr, options = {})
|
26
|
+
filter_map[attr] = {
|
27
|
+
as: :string,
|
28
|
+
option_text: "to_s",
|
29
|
+
option_value: "id",
|
30
|
+
collection: nil
|
31
|
+
}.merge(options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Godmin
|
2
|
+
module Resources
|
3
|
+
module ResourceService
|
4
|
+
module Ordering
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def apply_order(order_param, resources)
|
8
|
+
if order_param.present?
|
9
|
+
resources.order("#{resource_class.table_name}.#{order_column(order_param)} #{order_direction(order_param)}")
|
10
|
+
else
|
11
|
+
resources
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def order_column(order_param)
|
18
|
+
order_param.rpartition("_").first
|
19
|
+
end
|
20
|
+
|
21
|
+
def order_direction(order_param)
|
22
|
+
order_param.rpartition("_").last
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Godmin
|
2
|
+
module Resources
|
3
|
+
module ResourceService
|
4
|
+
module Pagination
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def apply_pagination(page_param, resources)
|
8
|
+
@paginator = Paginator.new(resources, per_page: per_page, current_page: page_param)
|
9
|
+
@paginator.paginate
|
10
|
+
end
|
11
|
+
|
12
|
+
def paginator
|
13
|
+
@paginator
|
14
|
+
end
|
15
|
+
|
16
|
+
def per_page
|
17
|
+
25
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Godmin
|
2
|
+
module Resources
|
3
|
+
module ResourceService
|
4
|
+
module Scopes
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
delegate :scope_map, to: "self.class"
|
8
|
+
|
9
|
+
def apply_scope(scope_param, resources)
|
10
|
+
return resources if scope_map.empty?
|
11
|
+
|
12
|
+
self.scope = scope_param
|
13
|
+
|
14
|
+
if scope && scope_map.key?(scope.to_sym)
|
15
|
+
send("scope_#{@scope}", resources)
|
16
|
+
else
|
17
|
+
fail NotImplementedError, "Scope #{@scope} not implemented"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def scope=(scope)
|
22
|
+
@scope = scope.blank? ? default_scope : scope
|
23
|
+
end
|
24
|
+
|
25
|
+
def scope
|
26
|
+
@scope
|
27
|
+
end
|
28
|
+
|
29
|
+
def scoped_by?(name)
|
30
|
+
@scope == name.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def scope_count(scope)
|
34
|
+
send("scope_#{scope}", resources_relation).count
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def default_scope
|
40
|
+
scope = scope_map.find -> { scope_map.first } do |_key, value|
|
41
|
+
value[:default] == true
|
42
|
+
end
|
43
|
+
|
44
|
+
scope ? scope[0].to_s : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
module ClassMethods
|
48
|
+
def scope_map
|
49
|
+
@scope_map ||= {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def scope(attr, options = {})
|
53
|
+
scope_map[attr] = {
|
54
|
+
default: false
|
55
|
+
}.merge(options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/godmin/version.rb
CHANGED
@@ -20,7 +20,7 @@ Dummy::Application.configure do
|
|
20
20
|
# config.action_dispatch.rack_cache = true
|
21
21
|
|
22
22
|
# Disable Rails's static asset server (Apache or nginx will already do this).
|
23
|
-
config.
|
23
|
+
config.serve_static_files = false
|
24
24
|
|
25
25
|
# Compress JavaScripts and CSS.
|
26
26
|
config.assets.js_compressor = :uglifier
|
@@ -13,7 +13,7 @@ Dummy::Application.configure do
|
|
13
13
|
config.eager_load = false
|
14
14
|
|
15
15
|
# Configure static asset server for tests with Cache-Control for performance.
|
16
|
-
config.
|
16
|
+
config.serve_static_files = true
|
17
17
|
config.static_cache_control = "public, max-age=3600"
|
18
18
|
|
19
19
|
# Show full error reports and disable caching.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# This file is auto-generated from the current state of the database. Instead
|
3
|
+
# of editing this file, please use the migrations feature of Active Record to
|
4
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
5
|
+
#
|
6
|
+
# Note that this schema.rb definition is the authoritative source for your
|
7
|
+
# database schema. If you need to create the application database on another
|
8
|
+
# system, you should be using db:schema:load, not running all the migrations
|
9
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
10
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
11
|
+
#
|
12
|
+
# It's strongly recommended that you check this file into your version control system.
|
13
|
+
|
14
|
+
ActiveRecord::Schema.define(version: 0) do
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
module Godmin
|
4
|
+
module Helpers
|
5
|
+
class FiltersTest < ActionView::TestCase
|
6
|
+
include BootstrapForm::Helper
|
7
|
+
include Godmin::Helpers::Filters
|
8
|
+
include Godmin::Helpers::Translations
|
9
|
+
|
10
|
+
def test_filter_form_is_a_bootstrap_form_builder
|
11
|
+
filter_form url: "/" do |f|
|
12
|
+
assert f.is_a? BootstrapForm::FormBuilder
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO: actually implement this test properly, apparently
|
17
|
+
# assert_select can be used if the helper output is parsed
|
18
|
+
# with HTML::Document first...
|
19
|
+
def test_string_filter_field
|
20
|
+
form = filter_form url: "/" do |f|
|
21
|
+
f.filter_field :foo, as: :string
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
module Godmin
|
4
|
+
class PaginatorTest < ActiveSupport::TestCase
|
5
|
+
def setup
|
6
|
+
resources_class = Class.new do
|
7
|
+
attr_reader :limit_param
|
8
|
+
attr_reader :offset_param
|
9
|
+
|
10
|
+
def count
|
11
|
+
50
|
12
|
+
end
|
13
|
+
|
14
|
+
def limit(limit_param)
|
15
|
+
@limit_param = limit_param
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def offset(offset_param)
|
20
|
+
@offset_param = offset_param
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@resources = resources_class.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_paginate
|
29
|
+
paginator = Paginator.new(@resources, per_page: 10, current_page: 1)
|
30
|
+
|
31
|
+
assert_equal @resources, paginator.paginate
|
32
|
+
assert_equal 10, @resources.limit_param
|
33
|
+
assert_equal 0, @resources.offset_param
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_paginate_with_offset
|
37
|
+
paginator = Paginator.new(@resources, per_page: 10, current_page: 2)
|
38
|
+
|
39
|
+
assert_equal @resources, paginator.paginate
|
40
|
+
assert_equal 10, @resources.limit_param
|
41
|
+
assert_equal 10, @resources.offset_param
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_current_page
|
45
|
+
paginator = Paginator.new(nil, current_page: 2)
|
46
|
+
assert_equal 2, paginator.current_page
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_current_page_when_no_page
|
50
|
+
paginator = Paginator.new(nil)
|
51
|
+
assert_equal 1, paginator.current_page
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_pages_when_pages_all_fit
|
55
|
+
paginator = Paginator.new(@resources, per_page: 10, current_page: 1)
|
56
|
+
assert_equal [1, 2, 3, 4, 5], paginator.pages
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_pages_when_pages_dont_fit_and_on_first_page
|
60
|
+
paginator = Paginator.new(@resources, per_page: 2, current_page: 1)
|
61
|
+
assert_equal [1, 2, 3, 4, 5, 6, 7], paginator.pages
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_pages_when_pages_dont_fit_and_on_middle_page
|
65
|
+
paginator = Paginator.new(@resources, per_page: 2, current_page: 7)
|
66
|
+
assert_equal [4, 5, 6, 7, 8, 9, 10], paginator.pages
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_pages_when_pages_dont_fit_and_on_last_page
|
70
|
+
paginator = Paginator.new(@resources, per_page: 2, current_page: 25)
|
71
|
+
assert_equal [19, 20, 21, 22, 23, 24, 25], paginator.pages
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_total_pages
|
75
|
+
paginator = Paginator.new(@resources, per_page: 10)
|
76
|
+
assert_equal 5, paginator.total_pages
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_total_resources
|
80
|
+
paginator = Paginator.new(@resources)
|
81
|
+
assert_equal 50, paginator.total_resources
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,28 +1,57 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
|
3
|
+
module Namespace
|
4
|
+
class ArticlePolicyTestPolicy; end
|
5
|
+
class ObjectPolicy; end
|
6
|
+
end
|
7
|
+
|
8
|
+
class ArticlePolicyTest; extend ActiveModel::Naming; end
|
9
|
+
class OverriddenPolicyTest
|
10
|
+
extend ActiveModel::Naming
|
11
|
+
def self.policy_class
|
12
|
+
Namespace::ObjectPolicy
|
13
|
+
end
|
14
|
+
|
15
|
+
def policy_class
|
16
|
+
Namespace::ObjectPolicy
|
17
|
+
end
|
18
|
+
end
|
4
19
|
|
5
20
|
module Godmin
|
6
21
|
module Authorization
|
7
22
|
class PolicyFinderTest < ActiveSupport::TestCase
|
8
23
|
def test_find_by_model
|
9
24
|
namespaced_as "namespace" do
|
10
|
-
policy = PolicyFinder.find(
|
11
|
-
assert_equal
|
25
|
+
policy = PolicyFinder.find(ArticlePolicyTest)
|
26
|
+
assert_equal Namespace::ArticlePolicyTestPolicy, policy
|
12
27
|
end
|
13
28
|
end
|
14
29
|
|
15
30
|
def test_find_by_class
|
16
31
|
namespaced_as "namespace" do
|
17
32
|
policy = PolicyFinder.find(Object)
|
18
|
-
assert_equal
|
33
|
+
assert_equal Namespace::ObjectPolicy, policy
|
19
34
|
end
|
20
35
|
end
|
21
36
|
|
22
37
|
def test_find_by_symbol
|
23
38
|
namespaced_as "namespace" do
|
24
|
-
policy = PolicyFinder.find(:
|
25
|
-
assert_equal
|
39
|
+
policy = PolicyFinder.find(:article_policy_test)
|
40
|
+
assert_equal Namespace::ArticlePolicyTestPolicy, policy
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_override_policy_class_on_class
|
45
|
+
namespaced_as "namespace" do
|
46
|
+
policy = PolicyFinder.find(OverriddenPolicyTest)
|
47
|
+
assert_equal Namespace::ObjectPolicy, policy
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_override_policy_class_on_instance
|
52
|
+
namespaced_as "namespace" do
|
53
|
+
policy = PolicyFinder.find(OverriddenPolicyTest.new)
|
54
|
+
assert_equal Namespace::ObjectPolicy, policy
|
26
55
|
end
|
27
56
|
end
|
28
57
|
end
|