active_manageable 0.1.1 → 0.2.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.
@@ -33,7 +33,7 @@ module ActiveManageable
33
33
  # the associations and options we cannot use the array extract_options! method
34
34
  # as this removes the last element in the array if it's a hash.
35
35
  #
36
- # For example :-
36
+ # For example:-
37
37
  # default_includes songs: :artist, loading_method: :preload, methods: :index
38
38
  # results in an argument value of :-
39
39
  # [{:songs=>:artist, :loading_method=>:preload, :methods=>:index}]
@@ -58,38 +58,36 @@ module ActiveManageable
58
58
  end
59
59
  end
60
60
 
61
- included do
62
- private
61
+ def default_includes(method: @current_method)
62
+ includes = defaults[:includes] || {}
63
+ associations = includes.dig(method.try(:to_sym), :associations) || includes.dig(:all, :associations)
64
+ associations.is_a?(Proc) ? instance_exec(&associations) : associations
65
+ end
63
66
 
64
- # Accepts either an array/hash of associations
65
- # or a hash with associations and loading_method keys
66
- # so it's possible to specify loading_method on a per request basis.
67
- # Uses associations and loading_method from opts
68
- # or defaults for the method or defaults for all methods
69
- # or configuration default loading_method.
70
- def includes(opts)
71
- unless opts.is_a?(Hash) && opts.key?(:associations)
72
- opts = {associations: opts}
73
- end
74
- associations = opts[:associations] || get_default_includes_associations
75
- if associations.present?
76
- loading_method = opts[:loading_method] || get_default_includes_loading_method
77
- @target = @target.send(loading_method, associations)
78
- else
79
- @target
80
- end
81
- end
67
+ def default_loading_method(method: @current_method)
68
+ includes = defaults[:includes] || {}
69
+ loading_method = includes.dig(method.try(:to_sym), :loading_method) || includes.dig(:all, :loading_method)
70
+ loading_method || ActiveManageable.configuration.default_loading_method
71
+ end
82
72
 
83
- def get_default_includes_associations
84
- includes = defaults[:includes] || {}
85
- associations = includes.dig(@current_method, :associations) || includes.dig(:all, :associations)
86
- associations.is_a?(Proc) ? instance_exec(&associations) : associations
87
- end
73
+ private
88
74
 
89
- def get_default_includes_loading_method
90
- includes = defaults[:includes] || {}
91
- loading_method = includes.dig(@current_method, :loading_method) || includes.dig(:all, :loading_method)
92
- loading_method || ActiveManageable.configuration.default_loading_method
75
+ # Accepts either an array/hash of associations
76
+ # or a hash with associations and loading_method keys
77
+ # so it's possible to specify loading_method on a per request basis.
78
+ # Uses associations and loading_method from opts
79
+ # or defaults for the method or defaults for all methods
80
+ # or configuration default loading_method.
81
+ def includes(opts)
82
+ unless opts.is_a?(Hash) && opts.key?(:associations)
83
+ opts = {associations: opts}
84
+ end
85
+ associations = opts[:associations] || default_includes
86
+ if associations.present?
87
+ loading_method = opts[:loading_method] || default_loading_method
88
+ @target = @target.send(loading_method, associations)
89
+ else
90
+ @target
93
91
  end
94
92
  end
95
93
  end
@@ -33,25 +33,23 @@ module ActiveManageable
33
33
  end
34
34
  end
35
35
 
36
- included do
37
- private
36
+ # Returns the default attribute values for the method
37
+ # from the class attribute that can contain a hash of attribute values
38
+ # or a lambda/proc to execute to return attribute values
39
+ def default_attribute_values(method: @current_method)
40
+ default_attributes = defaults[:attributes] || {}
41
+ attributes = default_attributes[method.try(:to_sym)] || default_attributes[:all] || {}
42
+ attributes = (instance_exec(&attributes) || {}) if attributes.is_a?(Proc)
43
+ attributes.with_indifferent_access
44
+ end
38
45
 
39
- # Returns attribute values to use in the new and create methods
40
- # consisting of a merge of the method attributes argument
41
- # and class defaults with the method argument taking precedence
42
- def attribute_values
43
- @attributes.is_a?(Hash) ? @attributes.reverse_merge(get_default_attribute_values) : @attributes
44
- end
46
+ private
45
47
 
46
- # Get the default attribute values for the method
47
- # from the class attribute that can contain a hash of attribute values
48
- # or a lambda/proc to execute to return attribute values
49
- def get_default_attribute_values
50
- default_attributes = defaults[:attributes] || {}
51
- attributes = default_attributes[@current_method] || default_attributes[:all] || {}
52
- attributes = (instance_exec(&attributes) || {}) if attributes.is_a?(Proc)
53
- attributes.with_indifferent_access
54
- end
48
+ # Returns attribute values to use in the new and create methods
49
+ # consisting of a merge of the method attributes argument
50
+ # and class defaults with the method argument taking precedence
51
+ def attribute_values
52
+ @attributes.is_a?(Hash) ? @attributes.reverse_merge(default_attribute_values) : @attributes
55
53
  end
56
54
  end
57
55
  end
@@ -19,23 +19,21 @@ module ActiveManageable
19
19
  end
20
20
  end
21
21
 
22
- included do
23
- private
22
+ # Returns the default order attributes from the class attribute
23
+ # that can contain an array of attribute names or name & direction strings
24
+ # or a lambda/proc to execute to return an array of attribute names
25
+ def default_order
26
+ defaults[:order].is_a?(Proc) ? instance_exec(&defaults[:order]) : defaults[:order]
27
+ end
24
28
 
25
- def order(attributes)
26
- @target = @target.order(get_order_attributes(attributes))
27
- end
29
+ private
28
30
 
29
- def get_order_attributes(attributes)
30
- attributes || get_default_order_attributes
31
- end
31
+ def order(attributes)
32
+ @target = @target.order(order_attributes(attributes))
33
+ end
32
34
 
33
- # Get the default order attributes from the class attribute
34
- # that can contain an array of attribute names or name & direction strings
35
- # or a lambda/proc to execute to return an array of attribute names
36
- def get_default_order_attributes
37
- defaults[:order].is_a?(Proc) ? instance_exec(&defaults[:order]) : defaults[:order]
38
- end
35
+ def order_attributes(attributes)
36
+ attributes || default_order
39
37
  end
40
38
  end
41
39
  end
@@ -20,37 +20,44 @@ module ActiveManageable
20
20
  end
21
21
  end
22
22
 
23
- included do
24
- private
23
+ # Returns the default scopes in a hash of hashes with the key containing the scope name
24
+ # and value containing an array of scope arguments.
25
+ #
26
+ # For example:-
27
+ # {rock: [], electronic: [], released_in_year: ["1980"]}
28
+ def default_scopes
29
+ get_scopes
30
+ end
25
31
 
26
- def scopes(scopes)
27
- get_scopes(scopes).each { |name, args| @target = @target.send(name, *args) }
28
- @target
29
- end
32
+ private
30
33
 
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]
34
+ def scopes(scopes)
35
+ get_scopes(scopes).each { |name, args| @target = @target.send(name, *args) }
36
+ @target
37
+ end
38
38
 
39
- Array.wrap(scopes).map do |scope|
40
- case scope
41
- when Symbol, String
42
- {scope => []}
43
- when Hash
44
- # ensure values are an array so they can be passed to the scope using splat operator
45
- scope.transform_values! { |v| Array.wrap(v) }
46
- when Proc
47
- # if the class default contains a lambda/proc that returns nil
48
- # don't call get_scopes as we don't want to end up in an infinite loop
49
- p_scopes = instance_exec(&scope)
50
- get_scopes(p_scopes) if p_scopes.present?
51
- end
52
- end.compact.reduce({}, :merge)
53
- end
39
+ # Accepts a symbol or string scope name, hash containing scope name and argument,
40
+ # lambda/proc to execute to return scope(s) or an array of those types
41
+ # and when the argument is blank it uses the class default scopes.
42
+ # Converts the scopes to a hash of hashes with the key containing the scope name
43
+ # and value containing an array of scope arguments.
44
+ def get_scopes(scopes = nil)
45
+ scopes ||= defaults[:scopes]
46
+
47
+ Array.wrap(scopes).map do |scope|
48
+ case scope
49
+ when Symbol, String
50
+ {scope => []}
51
+ when Hash
52
+ # ensure values are an array so they can be passed to the scope using splat operator
53
+ scope.transform_values! { |v| Array.wrap(v) }
54
+ when Proc
55
+ # if the class default contains a lambda/proc that returns nil
56
+ # don't call get_scopes as we don't want to end up in an infinite loop
57
+ p_scopes = instance_exec(&scope)
58
+ get_scopes(p_scopes) if p_scopes.present?
59
+ end
60
+ end.compact.reduce({}, :merge)
54
61
  end
55
62
  end
56
63
  end
@@ -24,21 +24,19 @@ module ActiveManageable
24
24
  end
25
25
  end
26
26
 
27
- included do
28
- private
27
+ # Returns the default select attributes for the method
28
+ # from the class attribute that can contain an array of attribute names
29
+ # or a lambdas/procs to execute to return an array of attribute names
30
+ def default_select(method: @current_method)
31
+ default_selects = defaults[:select] || {}
32
+ attributes = default_selects[method.try(:to_sym)] || default_selects[:all] || []
33
+ attributes.is_a?(Proc) ? instance_exec(&attributes) : attributes
34
+ end
29
35
 
30
- def select(attributes)
31
- @target = @target.select(attributes || get_default_select_attributes)
32
- end
36
+ private
33
37
 
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
38
+ def select(attributes)
39
+ @target = @target.select(attributes || default_select)
42
40
  end
43
41
  end
44
42
  end
@@ -20,29 +20,29 @@ module ActiveManageable
20
20
 
21
21
  included do
22
22
  class_attribute :unique_search, instance_writer: false, instance_predicate: false
23
+ end
23
24
 
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
25
+ def unique_search?
26
+ case unique_search
27
+ when nil
28
+ false
29
+ when TrueClass, FalseClass
30
+ unique_search
31
+ when Hash
32
+ evaluate_condition(*unique_search.first)
35
33
  end
34
+ end
35
+
36
+ private
36
37
 
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
38
+ def evaluate_condition(condition, method)
39
+ result = case method
40
+ when Symbol
41
+ method(method).call
42
+ when Proc
43
+ instance_exec(&method)
45
44
  end
45
+ condition == :if ? result : !result
46
46
  end
47
47
  end
48
48
  end
@@ -5,16 +5,31 @@ module ActiveManageable
5
5
 
6
6
  included do
7
7
  include ActiveManageable::Methods::Auxiliary::ModelAttributes
8
+ end
8
9
 
9
- def create(attributes:)
10
- initialize_state(attributes: attributes)
10
+ def create(attributes:)
11
+ initialize_state(attributes: attributes)
11
12
 
12
- @target = model_class.new(attribute_values)
13
- authorize(record: @target)
13
+ @target = build_object_for_create
14
+ authorize(record: @target)
14
15
 
15
- @target.save
16
+ model_class.transaction do
17
+ yield if block_given?
18
+ create_object
19
+ rescue ActiveRecord::RecordInvalid
20
+ false
16
21
  end
17
22
  end
23
+
24
+ private
25
+
26
+ def build_object_for_create
27
+ action_scope.new(attribute_values)
28
+ end
29
+
30
+ def create_object
31
+ @target.save!
32
+ end
18
33
  end
19
34
  end
20
35
  end
@@ -5,19 +5,34 @@ module ActiveManageable
5
5
 
6
6
  included do
7
7
  include ActiveManageable::Methods::Auxiliary::Includes
8
+ end
8
9
 
9
- def destroy(id:, options: {})
10
- initialize_state(options: options)
10
+ def destroy(id:, options: {})
11
+ initialize_state(options: options)
11
12
 
12
- @target = model_class
13
- includes(@options[:includes])
13
+ @target = action_scope
14
+ includes(@options[:includes])
14
15
 
15
- @target = @target.find(id)
16
- authorize(record: @target)
16
+ @target = find_object_for_destroy(id: id)
17
+ authorize(record: @target)
17
18
 
18
- @target.destroy
19
+ model_class.transaction do
20
+ yield if block_given?
21
+ destroy_object
22
+ rescue ActiveRecord::RecordNotDestroyed
23
+ false
19
24
  end
20
25
  end
26
+
27
+ private
28
+
29
+ def find_object_for_destroy(id:)
30
+ @target.find(id)
31
+ end
32
+
33
+ def destroy_object
34
+ @target.destroy!
35
+ end
21
36
  end
22
37
  end
23
38
  end
@@ -6,19 +6,27 @@ module ActiveManageable
6
6
  included do
7
7
  include ActiveManageable::Methods::Auxiliary::Includes
8
8
  include ActiveManageable::Methods::Auxiliary::Select
9
+ end
10
+
11
+ def edit(id:, options: {})
12
+ initialize_state(options: options)
13
+
14
+ @target = action_scope
15
+ includes(@options[:includes])
16
+ select(@options[:select])
9
17
 
10
- def edit(id:, options: {})
11
- initialize_state(options: options)
18
+ yield if block_given?
12
19
 
13
- @target = model_class
14
- includes(@options[:includes])
15
- select(@options[:select])
20
+ @target = find_object_for_edit(id: id)
21
+ authorize(record: @target)
22
+
23
+ @target
24
+ end
16
25
 
17
- @target = @target.find(id)
18
- authorize(record: @target)
26
+ private
19
27
 
20
- @target
21
- end
28
+ def find_object_for_edit(id:)
29
+ @target.find(id)
22
30
  end
23
31
  end
24
32
  end
@@ -9,40 +9,42 @@ module ActiveManageable
9
9
  include ActiveManageable::Methods::Auxiliary::Includes
10
10
  include ActiveManageable::Methods::Auxiliary::Select
11
11
  include ActiveManageable::Methods::Auxiliary::UniqueSearch
12
+ end
13
+
14
+ def index(options: {})
15
+ initialize_state(options: options)
12
16
 
13
- def index(options: {})
14
- initialize_state(options: options)
17
+ @target = authorization_scope
18
+ authorize(record: model_class)
19
+ search(@options[:search])
20
+ order(@options[:order])
21
+ scopes(@options[:scopes])
22
+ page(@options[:page])
23
+ includes(@options[:includes])
24
+ select(@options[:select])
25
+ distinct(unique_search?)
15
26
 
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?)
27
+ yield if block_given?
25
28
 
26
- @target
27
- end
29
+ @target
30
+ end
28
31
 
29
- private
32
+ private
30
33
 
31
- def scoped_class
32
- model_class
33
- end
34
+ def authorization_scope
35
+ action_scope
36
+ end
34
37
 
35
- def search(opts)
36
- @target
37
- end
38
+ def search(opts)
39
+ @target
40
+ end
38
41
 
39
- def page(opts)
40
- @target
41
- end
42
+ def page(opts)
43
+ @target
44
+ end
42
45
 
43
- def distinct(value)
44
- @target = target.distinct(value)
45
- end
46
+ def distinct(value)
47
+ @target = target.distinct(value)
46
48
  end
47
49
  end
48
50
  end
@@ -5,15 +5,23 @@ module ActiveManageable
5
5
 
6
6
  included do
7
7
  include ActiveManageable::Methods::Auxiliary::ModelAttributes
8
+ end
9
+
10
+ def new(attributes: {})
11
+ initialize_state(attributes: attributes)
12
+
13
+ @target = build_object_for_new
14
+ authorize(record: @target)
8
15
 
9
- def new(attributes: {})
10
- initialize_state(attributes: attributes)
16
+ yield if block_given?
17
+
18
+ @target
19
+ end
11
20
 
12
- @target = model_class.new(attribute_values)
13
- authorize(record: @target)
21
+ private
14
22
 
15
- @target
16
- end
23
+ def build_object_for_new
24
+ action_scope.new(attribute_values)
17
25
  end
18
26
  end
19
27
  end
@@ -6,19 +6,27 @@ module ActiveManageable
6
6
  included do
7
7
  include ActiveManageable::Methods::Auxiliary::Includes
8
8
  include ActiveManageable::Methods::Auxiliary::Select
9
+ end
10
+
11
+ def show(id:, options: {})
12
+ initialize_state(options: options)
13
+
14
+ @target = action_scope
15
+ includes(@options[:includes])
16
+ select(@options[:select])
9
17
 
10
- def show(id:, options: {})
11
- initialize_state(options: options)
18
+ yield if block_given?
12
19
 
13
- @target = model_class
14
- includes(@options[:includes])
15
- select(@options[:select])
20
+ @target = find_object_for_show(id: id)
21
+ authorize(record: @target)
22
+
23
+ @target
24
+ end
16
25
 
17
- @target = @target.find(id)
18
- authorize(record: @target)
26
+ private
19
27
 
20
- @target
21
- end
28
+ def find_object_for_show(id:)
29
+ @target.find(id)
22
30
  end
23
31
  end
24
32
  end
@@ -5,19 +5,40 @@ module ActiveManageable
5
5
 
6
6
  included do
7
7
  include ActiveManageable::Methods::Auxiliary::Includes
8
+ end
9
+
10
+ def update(id:, attributes:, options: {})
11
+ initialize_state(attributes: attributes, options: options)
8
12
 
9
- def update(id:, attributes:, options: {})
10
- initialize_state(attributes: attributes, options: options)
13
+ @target = action_scope
14
+ includes(@options[:includes])
11
15
 
12
- @target = model_class
13
- includes(@options[:includes])
16
+ @target = find_object_for_update(id: id)
17
+ authorize(record: @target)
14
18
 
15
- @target = @target.find(id)
16
- authorize(record: @target)
19
+ assign_attributes_for_update
17
20
 
18
- @target.update(@attributes)
21
+ model_class.transaction do
22
+ yield if block_given?
23
+ update_object
24
+ rescue ActiveRecord::RecordInvalid
25
+ false
19
26
  end
20
27
  end
28
+
29
+ private
30
+
31
+ def find_object_for_update(id:)
32
+ @target.find(id)
33
+ end
34
+
35
+ def assign_attributes_for_update
36
+ @target.assign_attributes(@attributes)
37
+ end
38
+
39
+ def update_object
40
+ @target.save!
41
+ end
21
42
  end
22
43
  end
23
44
  end
@@ -17,22 +17,42 @@ module ActiveManageable
17
17
  def default_page_size(page_size)
18
18
  defaults[:page] = {size: page_size.to_i}
19
19
  end
20
+
21
+ # Class option used when determining whether to create a paginatable collection without counting the total number of records
22
+ # within the order of precedence based on the (1) method option, or (2) class option, or (3) configuration option
23
+ def paginate_without_count(without_count = true)
24
+ self.without_count = without_count
25
+ end
20
26
  end
21
27
 
22
28
  included do
23
- private
29
+ class_attribute :without_count, instance_writer: false, instance_predicate: false
30
+ end
24
31
 
25
- def page(opts)
26
- @target = @target.page(page_number(opts)).per(page_size(opts))
27
- end
32
+ private
28
33
 
29
- def page_number(opts)
30
- opts.try(:[], :number)
34
+ def page(opts)
35
+ @target = @target.page(page_number(opts)).per(page_size(opts)).tap do |target|
36
+ target.without_count if paginate_without_count?(opts)
31
37
  end
38
+ end
32
39
 
33
- def page_size(opts)
34
- opts.try(:[], :size) || defaults.dig(:page, :size)
35
- end
40
+ def page_number(opts)
41
+ opts.try(:[], :number)
42
+ end
43
+
44
+ def page_size(opts)
45
+ opts.try(:[], :size) || defaults.dig(:page, :size)
46
+ end
47
+
48
+ # Determine whether to create a paginatable collection without counting the total number of records
49
+ # in order of precedence based on the (1) method option, or (2) class option, or (3) configuration option
50
+ def paginate_without_count?(opts)
51
+ [
52
+ opts.try(:[], :without_count),
53
+ without_count,
54
+ ActiveManageable.configuration.paginate_without_count
55
+ ].compact.first
36
56
  end
37
57
  end
38
58
  end