godmin 0.10.3 → 0.11.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.
- 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
|