active_manageable 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.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +52 -0
- data/.gitignore +20 -0
- data/.rspec +3 -0
- data/.rubocop.yml +42 -0
- data/.rubocop_rails.yml +201 -0
- data/.rubocop_rspec.yml +68 -0
- data/.standard.yml +5 -0
- data/Appraisals +27 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +194 -0
- data/LICENSE.txt +21 -0
- data/README.md +758 -0
- data/Rakefile +8 -0
- data/active_manageable.gemspec +75 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails_6_0.gemfile +8 -0
- data/gemfiles/rails_6_1.gemfile +8 -0
- data/gemfiles/rails_7_0.gemfile +8 -0
- data/lib/active_manageable/authorization/cancancan.rb +28 -0
- data/lib/active_manageable/authorization/pundit.rb +28 -0
- data/lib/active_manageable/base.rb +218 -0
- data/lib/active_manageable/configuration.rb +58 -0
- data/lib/active_manageable/methods/auxiliary/includes.rb +98 -0
- data/lib/active_manageable/methods/auxiliary/model_attributes.rb +59 -0
- data/lib/active_manageable/methods/auxiliary/order.rb +43 -0
- data/lib/active_manageable/methods/auxiliary/scopes.rb +59 -0
- data/lib/active_manageable/methods/auxiliary/select.rb +46 -0
- data/lib/active_manageable/methods/auxiliary/unique_search.rb +50 -0
- data/lib/active_manageable/methods/create.rb +20 -0
- data/lib/active_manageable/methods/destroy.rb +23 -0
- data/lib/active_manageable/methods/edit.rb +25 -0
- data/lib/active_manageable/methods/index.rb +49 -0
- data/lib/active_manageable/methods/new.rb +20 -0
- data/lib/active_manageable/methods/show.rb +25 -0
- data/lib/active_manageable/methods/update.rb +23 -0
- data/lib/active_manageable/pagination/kaminari.rb +39 -0
- data/lib/active_manageable/search/ransack.rb +38 -0
- data/lib/active_manageable/version.rb +5 -0
- data/lib/active_manageable.rb +43 -0
- metadata +373 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Auxiliary
|
4
|
+
module Scopes
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
# Sets the default scope(s) to use when fetching records in the index method
|
9
|
+
# if the index :options argument does not contain a :scopes key;
|
10
|
+
# accepting a scope name, a hash containing scope name and argument, or an array of names/hashes;
|
11
|
+
# also accepting a lambda/proc to execute to return a scope name or hash or array.
|
12
|
+
#
|
13
|
+
# For example:-
|
14
|
+
# default_scopes :electronic
|
15
|
+
# default_scopes {released_in_year: "1980"}
|
16
|
+
# default_scopes :rock, :electronic, {released_in_year: "1980"}
|
17
|
+
# default_scopes -> { index_scopes }
|
18
|
+
def default_scopes(*args)
|
19
|
+
defaults[:scopes] = args.first.is_a?(Proc) ? args.first : args
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
included do
|
24
|
+
private
|
25
|
+
|
26
|
+
def scopes(scopes)
|
27
|
+
get_scopes(scopes).each { |name, args| @target = @target.send(name, *args) }
|
28
|
+
@target
|
29
|
+
end
|
30
|
+
|
31
|
+
# Accepts a symbol or string scope name, hash containing scope name and argument,
|
32
|
+
# lambda/proc to execute to return scope(s) or an array of those types
|
33
|
+
# and when the argument is blank it uses the class default scopes.
|
34
|
+
# Converts the scopes to a hash of hashes with the key containing the scope name
|
35
|
+
# and value containing an array of scope arguments.
|
36
|
+
def get_scopes(scopes = nil)
|
37
|
+
scopes ||= defaults[:scopes]
|
38
|
+
scopes = scopes.is_a?(Hash) ? [scopes] : Array(scopes)
|
39
|
+
|
40
|
+
scopes.map do |scope|
|
41
|
+
case scope
|
42
|
+
when Symbol, String
|
43
|
+
{scope => []}
|
44
|
+
when Hash
|
45
|
+
# ensure values are an array so they can be passed to the scope using splat operator
|
46
|
+
scope.transform_values! { |v| Array(v) }
|
47
|
+
when Proc
|
48
|
+
# if the class default contains a lambda/proc that returns nil
|
49
|
+
# don't call get_scopes as we don't want to end up in an infinite loop
|
50
|
+
p_scopes = instance_exec(&scope)
|
51
|
+
get_scopes(p_scopes) if p_scopes.present?
|
52
|
+
end
|
53
|
+
end.compact.reduce({}, :merge)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Auxiliary
|
4
|
+
module Select
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
# Sets the default attributes to return in the SELECT statement used
|
9
|
+
# when fetching records in the index, show and edit methods
|
10
|
+
# if the methods :options argument does not contain a :select key;
|
11
|
+
# accepting either an array of attribute names or a lambda/proc
|
12
|
+
# to execute to return an array of attribute names;
|
13
|
+
# and optional :methods in which to use the attributes.
|
14
|
+
#
|
15
|
+
# For example:-
|
16
|
+
# default_select :name
|
17
|
+
# default_select :id, :name, methods: :show
|
18
|
+
# default_select -> { select_attributes }
|
19
|
+
# default_select -> { select_attributes }, methods: [:index, :edit]
|
20
|
+
def default_select(*attributes)
|
21
|
+
options = attributes.extract_options!.dup
|
22
|
+
attrs = attributes.first.is_a?(Proc) ? attributes.first : attributes
|
23
|
+
add_method_defaults(key: :select, value: attrs, methods: options[:methods])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
included do
|
28
|
+
private
|
29
|
+
|
30
|
+
def select(attributes)
|
31
|
+
@target = @target.select(attributes || get_default_select_attributes)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get the default select attributes for the method
|
35
|
+
# from the class attribute that can contain an array of attribute names
|
36
|
+
# or a lambdas/procs to execute to return an array of attribute names
|
37
|
+
def get_default_select_attributes
|
38
|
+
default_selects = defaults[:select] || {}
|
39
|
+
attributes = default_selects[@current_method] || default_selects[:all] || []
|
40
|
+
attributes.is_a?(Proc) ? instance_exec(&attributes) : attributes
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Auxiliary
|
4
|
+
module UniqueSearch
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
# Specifies whether to use the distinct method when fetching records in the index method;
|
9
|
+
# accepting no argument to always return unique records or a hash with :if or :unless keyword
|
10
|
+
# and method name or lambda/proc to execute each time the index method is called.
|
11
|
+
#
|
12
|
+
# For example:-
|
13
|
+
# has_unique_search
|
14
|
+
# has_unique_search if: :method_name
|
15
|
+
# has_unique_search unless: -> { lambda }
|
16
|
+
def has_unique_search(**args)
|
17
|
+
self.unique_search = args.present? ? args.assert_valid_keys(:if, :unless) : true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
included do
|
22
|
+
class_attribute :unique_search, instance_writer: false, instance_predicate: false
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def unique_search?
|
27
|
+
case unique_search
|
28
|
+
when nil
|
29
|
+
false
|
30
|
+
when TrueClass, FalseClass
|
31
|
+
unique_search
|
32
|
+
when Hash
|
33
|
+
evaluate_condition(*unique_search.first)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def evaluate_condition(condition, method)
|
38
|
+
result = case method
|
39
|
+
when Symbol
|
40
|
+
method(method).call
|
41
|
+
when Proc
|
42
|
+
instance_exec(&method)
|
43
|
+
end
|
44
|
+
condition == :if ? result : !result
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Create
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveManageable::Methods::Auxiliary::ModelAttributes
|
8
|
+
|
9
|
+
def create(attributes:)
|
10
|
+
initialize_state(attributes: attributes)
|
11
|
+
|
12
|
+
@target = model_class.new(attribute_values)
|
13
|
+
authorize(record: @target)
|
14
|
+
|
15
|
+
@target.save
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Destroy
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveManageable::Methods::Auxiliary::Includes
|
8
|
+
|
9
|
+
def destroy(id:, options: {})
|
10
|
+
initialize_state(options: options)
|
11
|
+
|
12
|
+
@target = model_class
|
13
|
+
includes(@options[:includes])
|
14
|
+
|
15
|
+
@target = @target.find(id)
|
16
|
+
authorize(record: @target)
|
17
|
+
|
18
|
+
@target.destroy
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Edit
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveManageable::Methods::Auxiliary::Includes
|
8
|
+
include ActiveManageable::Methods::Auxiliary::Select
|
9
|
+
|
10
|
+
def edit(id:, options: {})
|
11
|
+
initialize_state(options: options)
|
12
|
+
|
13
|
+
@target = model_class
|
14
|
+
includes(@options[:includes])
|
15
|
+
select(@options[:select])
|
16
|
+
|
17
|
+
@target = @target.find(id)
|
18
|
+
authorize(record: @target)
|
19
|
+
|
20
|
+
@target
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Index
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveManageable::Methods::Auxiliary::Order
|
8
|
+
include ActiveManageable::Methods::Auxiliary::Scopes
|
9
|
+
include ActiveManageable::Methods::Auxiliary::Includes
|
10
|
+
include ActiveManageable::Methods::Auxiliary::Select
|
11
|
+
include ActiveManageable::Methods::Auxiliary::UniqueSearch
|
12
|
+
|
13
|
+
def index(options: {})
|
14
|
+
initialize_state(options: options)
|
15
|
+
|
16
|
+
@target = scoped_class
|
17
|
+
authorize(record: model_class)
|
18
|
+
search(@options[:search])
|
19
|
+
order(@options[:order])
|
20
|
+
scopes(@options[:scopes])
|
21
|
+
page(@options[:page])
|
22
|
+
includes(@options[:includes])
|
23
|
+
select(@options[:select])
|
24
|
+
distinct(unique_search?)
|
25
|
+
|
26
|
+
@target
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def scoped_class
|
32
|
+
model_class
|
33
|
+
end
|
34
|
+
|
35
|
+
def search(opts)
|
36
|
+
@target
|
37
|
+
end
|
38
|
+
|
39
|
+
def page(opts)
|
40
|
+
@target
|
41
|
+
end
|
42
|
+
|
43
|
+
def distinct(value)
|
44
|
+
@target = target.distinct(value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module New
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveManageable::Methods::Auxiliary::ModelAttributes
|
8
|
+
|
9
|
+
def new(attributes: {})
|
10
|
+
initialize_state(attributes: attributes)
|
11
|
+
|
12
|
+
@target = model_class.new(attribute_values)
|
13
|
+
authorize(record: @target)
|
14
|
+
|
15
|
+
@target
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Show
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveManageable::Methods::Auxiliary::Includes
|
8
|
+
include ActiveManageable::Methods::Auxiliary::Select
|
9
|
+
|
10
|
+
def show(id:, options: {})
|
11
|
+
initialize_state(options: options)
|
12
|
+
|
13
|
+
@target = model_class
|
14
|
+
includes(@options[:includes])
|
15
|
+
select(@options[:select])
|
16
|
+
|
17
|
+
@target = @target.find(id)
|
18
|
+
authorize(record: @target)
|
19
|
+
|
20
|
+
@target
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveManageable
|
2
|
+
module Methods
|
3
|
+
module Update
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveManageable::Methods::Auxiliary::Includes
|
8
|
+
|
9
|
+
def update(id:, attributes:, options: {})
|
10
|
+
initialize_state(attributes: attributes, options: options)
|
11
|
+
|
12
|
+
@target = model_class
|
13
|
+
includes(@options[:includes])
|
14
|
+
|
15
|
+
@target = @target.find(id)
|
16
|
+
authorize(record: @target)
|
17
|
+
|
18
|
+
@target.update(@attributes)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#
|
2
|
+
# Pagination methods for Kaminari
|
3
|
+
# https://github.com/kaminari/kaminari
|
4
|
+
#
|
5
|
+
module ActiveManageable
|
6
|
+
module Pagination
|
7
|
+
module Kaminari
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
# Sets the default page size to use when fetching records in the index method
|
12
|
+
# if the index :options argument does not contain a :page hash with :size key;
|
13
|
+
# accepting an integer.
|
14
|
+
#
|
15
|
+
# For example:-
|
16
|
+
# default_page_size 5
|
17
|
+
def default_page_size(page_size)
|
18
|
+
defaults[:page] = {size: page_size.to_i}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
included do
|
23
|
+
private
|
24
|
+
|
25
|
+
def page(opts)
|
26
|
+
@target = @target.page(page_number(opts)).per(page_size(opts))
|
27
|
+
end
|
28
|
+
|
29
|
+
def page_number(opts)
|
30
|
+
opts.try(:[], :number)
|
31
|
+
end
|
32
|
+
|
33
|
+
def page_size(opts)
|
34
|
+
opts.try(:[], :size) || defaults.dig(:page, :size)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#
|
2
|
+
# Search methods for Ransack
|
3
|
+
# https://github.com/activerecord-hackery/ransack
|
4
|
+
#
|
5
|
+
module ActiveManageable
|
6
|
+
module Search
|
7
|
+
module Ransack
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
attr_reader :ransack
|
12
|
+
|
13
|
+
initialize_state_methods :initialize_ransack_state
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def initialize_ransack_state
|
18
|
+
@ransack = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def search(opts)
|
22
|
+
@ransack = @target.ransack(opts)
|
23
|
+
@target = @ransack.result
|
24
|
+
end
|
25
|
+
|
26
|
+
# Perform standard index module ordering when no ransack search params provided
|
27
|
+
# or no ransack sorts params provided
|
28
|
+
def order(attributes)
|
29
|
+
if @ransack.blank? || @ransack.sorts.empty?
|
30
|
+
@target = @target.order(get_order_attributes(attributes))
|
31
|
+
else
|
32
|
+
@target
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Active Support Core Extensions
|
4
|
+
# https://guides.rubyonrails.org/active_support_core_extensions.html
|
5
|
+
require "active_support/core_ext"
|
6
|
+
# rails-i18n required for "number.format.separator"
|
7
|
+
require "rails-i18n"
|
8
|
+
# flexitime required to parse date & datetime attribute values
|
9
|
+
require "flexitime"
|
10
|
+
|
11
|
+
require_relative "active_manageable/version"
|
12
|
+
require_relative "active_manageable/configuration"
|
13
|
+
require_relative "active_manageable/base"
|
14
|
+
require_relative "active_manageable/methods/index"
|
15
|
+
require_relative "active_manageable/methods/show"
|
16
|
+
require_relative "active_manageable/methods/new"
|
17
|
+
require_relative "active_manageable/methods/create"
|
18
|
+
require_relative "active_manageable/methods/edit"
|
19
|
+
require_relative "active_manageable/methods/update"
|
20
|
+
require_relative "active_manageable/methods/destroy"
|
21
|
+
require_relative "active_manageable/methods/auxiliary/includes"
|
22
|
+
require_relative "active_manageable/methods/auxiliary/model_attributes"
|
23
|
+
require_relative "active_manageable/methods/auxiliary/order"
|
24
|
+
require_relative "active_manageable/methods/auxiliary/scopes"
|
25
|
+
require_relative "active_manageable/methods/auxiliary/select"
|
26
|
+
require_relative "active_manageable/methods/auxiliary/unique_search"
|
27
|
+
require_relative "active_manageable/authorization/pundit"
|
28
|
+
require_relative "active_manageable/authorization/cancancan"
|
29
|
+
require_relative "active_manageable/search/ransack"
|
30
|
+
require_relative "active_manageable/pagination/kaminari"
|
31
|
+
|
32
|
+
module ActiveManageable
|
33
|
+
ALL_METHODS = "*"
|
34
|
+
|
35
|
+
thread_mattr_accessor :current_user
|
36
|
+
|
37
|
+
mattr_accessor :configuration
|
38
|
+
@@configuration = Configuration.new
|
39
|
+
|
40
|
+
def self.config
|
41
|
+
yield configuration
|
42
|
+
end
|
43
|
+
end
|