active_manageable 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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