wallaby-active_record 0.2.2 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (23) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/lib/adapters/wallaby/active_record/cancancan_provider.rb +1 -1
  4. data/lib/adapters/wallaby/active_record/default_provider.rb +1 -1
  5. data/lib/adapters/wallaby/active_record/model_decorator/fields_builder/association_builder.rb +6 -4
  6. data/lib/adapters/wallaby/active_record/model_decorator/fields_builder/polymorphic_builder.rb +3 -2
  7. data/lib/adapters/wallaby/active_record/model_decorator/fields_builder/sti_builder.rb +17 -13
  8. data/lib/adapters/wallaby/active_record/model_decorator/fields_builder.rb +3 -2
  9. data/lib/adapters/wallaby/active_record/model_decorator/title_field_finder.rb +6 -9
  10. data/lib/adapters/wallaby/active_record/model_decorator.rb +28 -26
  11. data/lib/adapters/wallaby/active_record/model_finder.rb +23 -25
  12. data/lib/adapters/wallaby/active_record/model_pagination_provider.rb +5 -13
  13. data/lib/adapters/wallaby/active_record/model_service_provider/permitter.rb +1 -1
  14. data/lib/adapters/wallaby/active_record/model_service_provider/querier/escaper.rb +14 -0
  15. data/lib/adapters/wallaby/active_record/model_service_provider/querier/transformer.rb +45 -13
  16. data/lib/adapters/wallaby/active_record/model_service_provider/querier/wrapper.rb +8 -6
  17. data/lib/adapters/wallaby/active_record/model_service_provider/querier.rb +3 -3
  18. data/lib/adapters/wallaby/active_record/model_service_provider/validator.rb +6 -4
  19. data/lib/adapters/wallaby/active_record/model_service_provider.rb +8 -17
  20. data/lib/adapters/wallaby/active_record/pundit_provider.rb +2 -2
  21. data/lib/adapters/wallaby/active_record.rb +2 -1
  22. data/lib/wallaby/active_record/version.rb +1 -1
  23. metadata +8 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93579d0cc4c2a81356d19acc3a24a7cac7160406f4c09c829de95f10d3db1020
4
- data.tar.gz: d591a516f5c2fc04d7b6f46af992498e093d5d3f706d03c639a77a11eeeba339
3
+ metadata.gz: 55cc61191c3d68eabf0bb98b4977d10c7d5063736e95cba4b4cb446bf1ecebb7
4
+ data.tar.gz: 8d03e8a115f52bd386e87bd6ba2a8a95fdec39743a1a040396f8f1b06ac1c0fe
5
5
  SHA512:
6
- metadata.gz: 9197ae20142ea0190ed68314ef6eb5c7ec4915febe072f62f0144bb6f42dddbada38adadf05b83eba0ed9744d2c3bc050f2836b25a46c394ab553decaa4f0dee
7
- data.tar.gz: 0a59dcd809acb177822b3665b576b18eefdb0ef55b4fdcfdc6a3cf9344ce73fa1ddc4cb608c47de4d630b6149d0f1a36569eee8c63253830ddad92aae3c3f514
6
+ metadata.gz: '0685f26c6acf1c511be09cfe68258bce7ca3cd01263047205fcdbfc2e81da7ca1239af3f0725510401d02f6a2c2629003e79a41760a8e672acaa710cd566d8db'
7
+ data.tar.gz: fa1497666790dba1d421f1194ce3e3ffc4851e3425a7a2da5701af9f5d4573d1df9bbf3a9ee656aa3490f81ed363af4a6758121d6c3cbd761f3d52707f84f6bb
data/README.md CHANGED
@@ -7,7 +7,8 @@
7
7
  [![Test Coverage](https://api.codeclimate.com/v1/badges/9ba0a610043a2e1a9e74/test_coverage)](https://codeclimate.com/github/wallaby-rails/wallaby-active_record/test_coverage)
8
8
  [![Inch CI](https://inch-ci.org/github/wallaby-rails/wallaby-active_record.svg?branch=master)](https://inch-ci.org/github/wallaby-rails/wallaby-active_record)
9
9
 
10
- Wallaby::ActiveRecord is the ActiveRecord adapter for [Wallaby::Core](https://github.com/wallaby-rails/wallaby-core).
10
+ Wallaby::ActiveRecord is the ActiveRecord adapter that implements the [Wallaby::Core](https://github.com/wallaby-rails/wallaby-core)
11
+ interfaces to handle ActiveRecord model/instance(s) in all the CRUD/authorization/pagination operations.
11
12
 
12
13
  ## Install
13
14
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Wallaby
4
4
  class ActiveRecord
5
- # Cancancan provider for ActiveRecord
5
+ # Cancancan provider for {Wallaby::ActiveRecord}
6
6
  class CancancanProvider < CancancanAuthorizationProvider
7
7
  end
8
8
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Wallaby
4
4
  class ActiveRecord
5
- # Default provider for ActiveRecord
5
+ # Default provider for {Wallaby::ActiveRecord}
6
6
  class DefaultProvider < DefaultAuthorizationProvider
7
7
  end
8
8
  end
@@ -4,7 +4,7 @@ module Wallaby
4
4
  class ActiveRecord
5
5
  class ModelDecorator
6
6
  class FieldsBuilder
7
- # To build the metadata for associations
7
+ # To build the metadata for associations fields
8
8
  class AssociationBuilder
9
9
  # Update the metadata
10
10
  # @param metadata [Hash]
@@ -18,7 +18,7 @@ module Wallaby
18
18
  metadata[:foreign_key] = foreign_key_for(reflection, type)
19
19
  end
20
20
 
21
- private
21
+ protected
22
22
 
23
23
  # @param reflection [ActiveRecord::Reflection]
24
24
  # @param type [Symbol]
@@ -35,13 +35,15 @@ module Wallaby
35
35
  end
36
36
 
37
37
  # @param reflection [ActiveRecord::Reflection]
38
- # @return [Boolean] whether it's a through relation
38
+ # @return [true] if it's a through relation
39
+ # @return [false] otherwise
39
40
  def through?(reflection)
40
41
  reflection.is_a? ::ActiveRecord::Reflection::ThroughReflection
41
42
  end
42
43
 
43
44
  # @param reflection [ActiveRecord::Reflection]
44
- # @return [Boolean] whether it has scope
45
+ # @return [true] if it has scope
46
+ # @return [false] otherwise
45
47
  def scope?(reflection)
46
48
  reflection.scope.present?
47
49
  end
@@ -19,7 +19,7 @@ module Wallaby
19
19
  end
20
20
  end
21
21
 
22
- private
22
+ protected
23
23
 
24
24
  # @param reflection [ActiveRecord::Reflection]
25
25
  # @return [Array<Class>] a list of classes for this polymorphism
@@ -38,7 +38,8 @@ module Wallaby
38
38
 
39
39
  # @param model_class [Class]
40
40
  # @param polymorphic_name [String] polymorphic name
41
- # @return [Boolean] if polymorphism defined?
41
+ # @return [true] if polymorphism defined?
42
+ # @return [false] otherwise
42
43
  def polymorphic_defined?(model_class, polymorphic_name)
43
44
  polymorphic_name_sym = polymorphic_name.try(:to_sym)
44
45
  model_class.reflections.any? do |_field_name, reflection|
@@ -4,44 +4,48 @@ module Wallaby
4
4
  class ActiveRecord
5
5
  class ModelDecorator
6
6
  class FieldsBuilder
7
- # To build the metadata for sti column
7
+ # This class updates the field metadata's value of **type** and **sti_class_list**
8
+ # for STI (Single Table Inheritance) model
8
9
  class StiBuilder
9
10
  # @param model_class [Class]
10
11
  def initialize(model_class)
11
12
  @model_class = model_class
12
13
  end
13
14
 
14
- # update the metadata
15
+ # Update the field metadata's value for **type** and **sti_class_list**
15
16
  # @param metadata [Hash]
16
17
  # @param column [ActiveRecord::ConnectionAdapters::Column]
17
18
  def update(metadata, column)
18
19
  return unless @model_class.inheritance_column == column.name
19
20
 
20
21
  metadata[:type] = 'sti'
21
- metadata[:sti_class_list] = sti_list(find_parent_of(@model_class))
22
+ metadata[:sti_class_list] = sti_list(find_sti_parent_of(@model_class))
22
23
  end
23
24
 
24
- private
25
+ protected
25
26
 
27
+ # Return the alphabet-order STI list
28
+ # by traversing the inheritance tree for given model.
26
29
  # @param klass [Class]
27
- # @return [Array<Class>] a list of STI classes for this model
30
+ # @return [Array<Class>]
28
31
  def sti_list(klass)
29
- list = klass.descendants << klass
30
- list.sort_by(&:name)
32
+ (klass.descendants << klass).sort_by(&:name)
31
33
  end
32
34
 
35
+ # Find out which parent is the one that can give us the STI list.
33
36
  # @param klass [Class]
34
- # @return [Class] the top parent class in the STI hierarchy
35
- def find_parent_of(klass)
37
+ # @return [Class]
38
+ def find_sti_parent_of(klass)
36
39
  parent = klass
37
- parent = parent.superclass until top_parent?(parent.superclass)
40
+ parent = parent.superclass until not_sti_parent?(parent.superclass)
38
41
  parent
39
42
  end
40
43
 
41
44
  # @param klass [Class]
42
- # @return [Boolean] whether the class is ActiveRecord base class
43
- def top_parent?(klass)
44
- klass == ModelFinder.base || klass.try(:abstract_class?)
45
+ # @return [true] if klass is ActiveRecord::Base or abstract
46
+ # @return [false] otherwise
47
+ def not_sti_parent?(klass)
48
+ klass == ::ActiveRecord::Base || klass.try(:abstract_class?)
45
49
  end
46
50
  end
47
51
  end
@@ -3,7 +3,7 @@
3
3
  module Wallaby
4
4
  class ActiveRecord
5
5
  class ModelDecorator
6
- # To search and build the metadata for fields
6
+ # To build the metadata for fields
7
7
  class FieldsBuilder
8
8
  # @param model_class [Class]
9
9
  def initialize(model_class)
@@ -22,7 +22,8 @@ module Wallaby
22
22
  end
23
23
  end
24
24
 
25
- # @return [Hash<String, Hash>] a hash for general fields
25
+ # @return [Hash<String, Hash>] a hash for association fields
26
+ # (e.g. belongs_to / has_one / has_many / has_and_belongs_to_many)
26
27
  def association_fields
27
28
  @model_class.reflections.each_with_object({}) do |(name, ref), fields|
28
29
  metadata = {
@@ -16,15 +16,12 @@ module Wallaby
16
16
 
17
17
  # @return [String] field name that can be used as title
18
18
  def find
19
- possible_title_fields = @fields.select do |_field_name, metadata|
20
- TITLE_FIELD_TYPES.include? metadata[:type]
21
- end
22
- target_field = possible_title_fields.keys.find do |field_name|
23
- TITLE_NAMES.any? { |v| field_name.to_s.index v }
24
- end
25
- target_field \
26
- || possible_title_fields.keys.first \
27
- || @model_class.primary_key
19
+ FieldUtils.first_field_by(
20
+ {
21
+ name: /title|name|label|string/,
22
+ type: 'string'
23
+ }, @fields
24
+ ) || @model_class.primary_key
28
25
  end
29
26
  end
30
27
  end
@@ -2,24 +2,25 @@
2
2
 
3
3
  module Wallaby
4
4
  class ActiveRecord
5
- # Modal decorator for ActiveRecord
5
+ # Modal decorator for {Wallaby::ActiveRecord}
6
6
  class ModelDecorator < ::Wallaby::ModelDecorator
7
- # Data types to exclude for index page
8
- INDEX_EXCLUSIVE_DATA_TYPES =
9
- (['', 'medium', 'long'] * 2)
10
- .zip(%w(blob text) * 3).map(&:join)
11
- .concat(%w(binary citext hstore json jsonb tsvector xml)).freeze
7
+ # Data types to exclude for {#index_field_names}
8
+ INDEX_EXCLUSIVE_DATA_TYPES = %w(
9
+ binary citext hstore json jsonb tsvector xml
10
+ blob mediumblob longblob text mediumtext longtext
11
+ ).freeze
12
12
 
13
- # Class to exclude for show page
13
+ # Classes to exclude for {#show_field_names}
14
14
  SHOW_EXCLUSIVE_CLASS_NAMES = %w(ActiveStorage::Attachment ActiveStorage::Blob).freeze
15
15
 
16
- # Data types to exclude for form page
16
+ # Fields to exclude for {#form_field_names}
17
17
  FORM_EXCLUSIVE_DATA_TYPES = %w(created_at updated_at).freeze
18
18
 
19
- # Origin metadata directly coming from ActiveRecord.
19
+ # Original metadata information of the primative and association fields
20
+ # pulling out from the ActiveRecord model.
20
21
  #
21
- # It needs to be frozen so that we can keep the metadata integrity
22
- # @example sample fields:
22
+ # It needs to be frozen so that we can keep the metadata intact.
23
+ # @example sample fields metadata:
23
24
  # model_decorator.fields
24
25
  # # =>
25
26
  # {
@@ -27,28 +28,29 @@ module Wallaby
27
28
  # id: { name: 'id', type: 'integer', label: 'Id' },
28
29
  # # association field
29
30
  # category: {
30
- # 'name' => 'category',
31
- # 'type' => 'belongs_to',
32
- # 'label' => 'Category',
33
- # 'is_association' => true,
34
- # 'is_through' => false,
35
- # 'has_scope' => false,
36
- # 'foreign_key' => 'category_id',
37
- # 'class' => Category
31
+ # name: 'category',
32
+ # type: 'belongs_to',
33
+ # label: 'Category',
34
+ # is_association: true,
35
+ # is_through: false,
36
+ # has_scope: false,
37
+ # foreign_key: 'category_id',
38
+ # class: Category
38
39
  # }
39
40
  # }
40
41
  # @return [ActiveSupport::HashWithIndifferentAccess] metadata
41
42
  def fields
43
+ # NOTE: Need to check the database and table's existence before building up the metadata
44
+ # so that the database creation and migration related task can be executed.
42
45
  @fields ||= ::ActiveSupport::HashWithIndifferentAccess.new.tap do |hash|
43
- # NOTE: Need to check database and table's existence
44
- # before pulling out the metadata from model.
45
- # So that the database and migration related task can be executed.
46
- next unless ::ActiveRecord::Base.connected? && @model_class.table_exists?
46
+ next hash.default = {} unless @model_class.table_exists?
47
47
 
48
48
  hash.merge! general_fields
49
49
  hash.merge! association_fields
50
50
  hash.except!(*foreign_keys_from_associations)
51
51
  end.freeze
52
+ rescue ::ActiveRecord::NoDatabaseError
53
+ Hash.new({}).with_indifferent_access
52
54
  end
53
55
 
54
56
  # A copy of {#fields} for index page
@@ -86,7 +88,8 @@ module Wallaby
86
88
  end.keys
87
89
  end
88
90
 
89
- # @return [Array<String>] a list of field names for form (new/edit) page (note: complex fields are excluded).
91
+ # @return [Array<String>] a list of field names for form (new/edit) page
92
+ # (note: timestamps fields (e.g. created_at/updated_at) and complex relation fields are excluded).
90
93
  def form_field_names
91
94
  @form_field_names ||=
92
95
  form_fields.reject do |field_name, metadata|
@@ -109,9 +112,8 @@ module Wallaby
109
112
  # To guess the title for resource.
110
113
  #
111
114
  # It will go through the fields and try to find out the one that looks
112
- # like a name or text to represent this resource. Otherwise, it will fall
115
+ # like a name or text representing this resource. Otherwise, it will fall
113
116
  # back to primary key.
114
- #
115
117
  # @param resource [Object]
116
118
  # @return [String] the title of given resource
117
119
  def guess_title(resource)
@@ -2,43 +2,41 @@
2
2
 
3
3
  module Wallaby
4
4
  class ActiveRecord
5
- # Model finder
5
+ # Finder to return all the appropriate ActiveRecord models.
6
6
  class ModelFinder < ::Wallaby::ModelFinder
7
- # @return [Array<Class>] a list of ActiveRecord subclasses
7
+ # Return a list of ActiveRecord::Base subclasses that aren't one of the following types:
8
+ #
9
+ # 1. abstract class
10
+ # 2. anonymous class
11
+ # 3. the HABTM relation class
12
+ # @return [Array<Class>]
8
13
  def all
9
- self.class.base.descendants.reject do |model_class|
10
- abstract?(model_class) || anonymous?(model_class) || habtm?(model_class)
14
+ ::ActiveRecord::Base.descendants.reject do |model_class|
15
+ defined?(::ApplicationRecord) && model_class == ::ApplicationRecord ||
16
+ model_class.abstract_class? ||
17
+ anonymous?(model_class) ||
18
+ model_class.name.index('HABTM') ||
19
+ invalid_class_name?(model_class)
11
20
  end.sort_by(&:to_s)
12
21
  end
13
22
 
14
- # This is only for ActiveRecord
15
- # @return [ApplicationRecord, ActiveRecord::Base] base ActiveRecord class
16
- def self.base
17
- return ::ApplicationRecord if defined? ::ApplicationRecord
23
+ protected
18
24
 
19
- ::ActiveRecord::Base
20
- end
21
-
22
- private
23
-
24
- # Is model class abstract?
25
25
  # @param model_class [Class]
26
- # @return [Boolean]
27
- def abstract?(model_class)
28
- model_class.abstract_class?
29
- end
30
-
31
26
  # @see Wallaby::ModuleUtils.anonymous_class?
32
27
  def anonymous?(model_class)
33
- ModuleUtils.anonymous_class? model_class
28
+ ModuleUtils.anonymous_class?(model_class).tap do |result|
29
+ Logger.warn "Anonymous class is detected for table #{model_class.try :table_name}" if result
30
+ end
34
31
  end
35
32
 
36
- # Check and see if given model class is intermediate class that generated
37
- # for has and belongs to many assocation
33
+ # To exclude classes that have invalid class name, e.g. **primary::SchemaMigration** from Rails test
38
34
  # @param model_class [Class]
39
- # @return [Boolean]
40
- def habtm?(model_class)
41
- model_class.name.index('HABTM')
35
+ def invalid_class_name?(model_class)
36
+ model_class.name.constantize
37
+ false
38
+ rescue NameError
39
+ true
42
40
  end
43
41
  end
44
42
  end
@@ -8,25 +8,17 @@ module Wallaby
8
8
  # @return [true] if paginatable
9
9
  # @return [false] if not paginatable
10
10
  def paginatable?
11
- paginatable =
12
- # kaminari
13
- @collection.respond_to?(:total_count) || \
14
- @collection.respond_to?(:total_entries) # will_paginate
15
- Rails.logger.warn "#{@collection.inspect} is not paginatable.\nfrom #{__FILE__}:#{__LINE__}" unless paginatable
16
-
17
- paginatable
11
+ (@collection.respond_to?(:unscope) && @collection.respond_to?(:count)).tap do |paginatable|
12
+ Logger.warn "#{@collection} is not paginatable." unless paginatable
13
+ end
18
14
  end
19
15
 
20
16
  # @return [Integer] total count for the collection
21
17
  def total
22
- # kaminari
23
- @collection.try(:total_count) || \
24
- @collection.try(:total_entries) # will_paginate
18
+ @collection.unscope(:offset, :limit).count
25
19
  end
26
20
 
27
- # @return [Integer] page size from parameters or
28
- # {https://rubydoc.info/gems/wallaby-core/Wallaby/Configuration/Pagination#page_size-instance_method page_size}
29
- # Wallaby configuration
21
+ # @return [Integer] page size from parameters or Wallaby configuration
30
22
  def page_size
31
23
  (@params[:per] || Wallaby.configuration.pagination.page_size).to_i
32
24
  end
@@ -3,7 +3,7 @@
3
3
  module Wallaby
4
4
  class ActiveRecord
5
5
  class ModelServiceProvider
6
- # Filter the params
6
+ # Whitelist the params for mass-assignment
7
7
  class Permitter
8
8
  # @param model_decorator [Wallaby::ModelDecorator]
9
9
  def initialize(model_decorator)
@@ -11,6 +11,14 @@ module Wallaby
11
11
  PCT = '%' # :nodoc:
12
12
 
13
13
  class << self
14
+ # @example Return the escaped keyword if the first/last char of the keyword is `%`/`_`
15
+ # Wallaby::ActiveRecord::ModelServiceProvider::Querier::Escaper.execute('%something_else%')
16
+ # # => '%something\_else%'
17
+ # @example Return the escaped keyword wrapped with `%` if the first/last char of the keyword is NOT `%`/`_`
18
+ # Wallaby::ActiveRecord::ModelServiceProvider::Querier::Escaper.execute('keyword')
19
+ # # => '%keyword%'
20
+ # @param keyword [String]
21
+ # @return [String] escaped string for LIKE query
14
22
  def execute(keyword)
15
23
  first = keyword.first
16
24
  last = keyword.last
@@ -23,6 +31,12 @@ module Wallaby
23
31
  "#{starting}#{escaped}#{ending}"
24
32
  end
25
33
 
34
+ protected
35
+
36
+ # @param first_condition [Boolean] first condition
37
+ # @param first_char [Boolean] first char
38
+ # @param second_condition [nil, String] second condition
39
+ # @param default_sign [String]
26
40
  def sign(
27
41
  first_condition, first_char, second_condition, default_sign = PCT
28
42
  )
@@ -34,9 +34,21 @@ module Wallaby
34
34
  ':!()' => :not_between
35
35
  }.freeze
36
36
 
37
+ SEQUENCE_JOIN_OPERATORS = { # :nodoc:
38
+ ':' => :or,
39
+ ':=' => :or,
40
+ ':!' => :and,
41
+ ':!=' => :and,
42
+ ':<>' => :and
43
+ }.freeze
44
+
45
+ BETWEEN_OPERATORS = { # :nodoc:
46
+ ':()' => true,
47
+ ':!()' => true
48
+ }.freeze
49
+
37
50
  # For single null
38
51
  rule null: simple(:value)
39
- rule null: sequence(:value)
40
52
 
41
53
  # For single boolean
42
54
  rule(boolean: simple(:value)) { /true/i.match? value }
@@ -51,8 +63,7 @@ module Wallaby
51
63
  rule left: simple(:left), op: simple(:op), right: simple(:right) do
52
64
  oped = op.try :to_str
53
65
  operator = SIMPLE_OPERATORS[oped]
54
- # skip if the operator is unknown
55
- next unless operator
66
+ next Transformer.warn "Unknown operator #{oped} for %<exp>s", instance_values unless operator
56
67
 
57
68
  lefted = left.try :to_str
58
69
  convert =
@@ -68,24 +79,45 @@ module Wallaby
68
79
  rule left: simple(:left), op: simple(:op), right: sequence(:right) do
69
80
  oped = op.try :to_str
70
81
  operator = SEQUENCE_OPERATORS[oped]
71
- next unless operator
82
+ next Transformer.warn "Unknown operator #{oped} for %<exp>s", instance_values unless operator
72
83
 
73
84
  exps = Wrapper.new
74
85
  lefted = left.try :to_str
75
- if right.include? nil
76
- nil_operator = SIMPLE_OPERATORS[oped]
77
- next unless nil_operator
86
+ if BETWEEN_OPERATORS[oped] # BETWEEN related operators
87
+ next Transformer.warn 'Invalid values for %<exp>s', instance_values unless right.first && right.second
78
88
 
79
- exps.push left: lefted, op: nil_operator, right: right.delete(nil)
89
+ convert = Range.new right.first, right.second
90
+ exps.push left: lefted, op: operator, right: convert
91
+ else
92
+ join = SEQUENCE_JOIN_OPERATORS[oped]
93
+ if right.include? nil
94
+ exps.push left: lefted, op: SIMPLE_OPERATORS[oped], right: right.delete(nil), join: join
95
+ end
96
+ exps.push left: lefted, op: operator, right: right, join: join
80
97
  end
81
- convert = Range.new right.try(:first), right.try(:second) if %w(:() :!()).include?(oped)
82
- exps.push left: lefted, op: operator, right: convert || right
83
98
  exps
84
99
  end
85
100
 
86
- def transform(query_string)
87
- result = apply Parser.new.parse(query_string || EMPTY_STRING)
88
- result.is_a?(Array) ? result : [result]
101
+ class << self
102
+ # @param query_string [String]
103
+ # @return [Array]
104
+ def execute(query_string)
105
+ result = new.apply Parser.new.parse(query_string || EMPTY_STRING)
106
+ result.is_a?(Array) ? result : [result]
107
+ end
108
+
109
+ # @param message [String]
110
+ # @param exp [Hash,nil] transformed expression
111
+ # @return [nil]
112
+ def warn(message, exp = nil)
113
+ Logger.warn message, exp: to_origin(exp), sourcing: 2
114
+ end
115
+
116
+ # @param exp [Hash,nil] transformed expression
117
+ # @return [String] origin expression
118
+ def to_origin(exp)
119
+ "'#{exp['left']}#{exp['op']}#{exp['right']}'"
120
+ end
89
121
  end
90
122
  end
91
123
  end
@@ -4,18 +4,20 @@ module Wallaby
4
4
  class ActiveRecord
5
5
  class ModelServiceProvider
6
6
  class Querier
7
- # Build up query using the results
7
+ # Wrapper for the {Wallaby::ActiveRecord::ModelServiceProvider::Querier::Transformer} result.
8
+ # It's only used by {Wallaby::ActiveRecord::ModelServiceProvider::Querier} to tell it apart
9
+ # with non-{Wallaby::ActiveRecord::ModelServiceProvider::Querier::Transformer} result
8
10
  class Wrapper
9
11
  attr_reader :list
10
- delegate :push, :each, to: :list
12
+ delegate :push, to: :list
13
+ delegate :each, to: :list
14
+ delegate :last, to: :list
15
+ delegate :[], to: :last
11
16
 
17
+ # @param list [Array]
12
18
  def initialize(list = [])
13
19
  @list = list
14
20
  end
15
-
16
- def [](key)
17
- list.last[key]
18
- end
19
21
  end
20
22
  end
21
23
  end
@@ -26,7 +26,7 @@ module Wallaby
26
26
  scope.where query
27
27
  end
28
28
 
29
- private
29
+ protected
30
30
 
31
31
  # @return [Arel::Table] arel table
32
32
  def table
@@ -36,7 +36,7 @@ module Wallaby
36
36
  # @param params [ActionController::Parameters]
37
37
  # @return [Array<String, Array, Array>] filter_name, keywords, field_queries
38
38
  def extract(params)
39
- expressions = Transformer.new.transform params[:q]
39
+ expressions = Transformer.execute params[:q]
40
40
  keywords = expressions.select { |v| v.is_a? String }
41
41
  field_queries = expressions.select { |v| v.is_a? Wrapper }
42
42
  filter_name = params[:filter]
@@ -149,7 +149,7 @@ module Wallaby
149
149
  query = nil
150
150
  exps.each do |exp|
151
151
  sub = table[exp[:left]].try(exp[:op], exp[:right])
152
- query = query.try(:or, sub) || sub
152
+ query = query.try(exp[:join], sub) || sub
153
153
  end
154
154
  query
155
155
  end
@@ -3,7 +3,7 @@
3
3
  module Wallaby
4
4
  class ActiveRecord
5
5
  class ModelServiceProvider
6
- # Validator
6
+ # Validate values for record create / update
7
7
  class Validator
8
8
  # @param model_decorator [Wallaby::ModelDecorator]
9
9
  def initialize(model_decorator)
@@ -11,7 +11,8 @@ module Wallaby
11
11
  end
12
12
 
13
13
  # @param resource [Object] resource object
14
- # @return [Boolean] whether the resource object is valid
14
+ # @return [true] if the resource object is valid
15
+ # @return [false] otherwise
15
16
  def valid?(resource)
16
17
  resource.attributes.each do |field_name, values|
17
18
  metadata = @model_decorator.fields[field_name]
@@ -22,10 +23,11 @@ module Wallaby
22
23
  resource.errors.blank?
23
24
  end
24
25
 
25
- private
26
+ protected
26
27
 
27
28
  # @param values [Array]
28
- # @return [Boolean] whether the values are valid range values
29
+ # @return [true] if the values are valid range values
30
+ # @return [false] otherwise
29
31
  def valid_range_type?(values, metadata)
30
32
  !metadata \
31
33
  || !%w(daterange tsrange tstzrange).include?(metadata[:type]) \
@@ -8,7 +8,6 @@ module Wallaby
8
8
  # @param action [String, Symbol]
9
9
  # @param authorizer
10
10
  # @return [ActionController::Parameters] whitelisted parameters
11
- # @see Wallaby::ModelServiceProvider#permit
12
11
  def permit(params, action, authorizer)
13
12
  authorized_fields = authorizer.permit_params action, @model_class
14
13
  params.require(param_key).permit(authorized_fields || permitted_fields)
@@ -18,7 +17,6 @@ module Wallaby
18
17
  # @param params [ActionController::Parameters]
19
18
  # @param authorizer [Ability] for now
20
19
  # @return [ActiveRecord::Relation] relation
21
- # @see Wallaby::ModelServiceProvider#collection
22
20
  def collection(params, authorizer)
23
21
  query = querier.search params
24
22
  query = query.order params[:sort] if params[:sort].present?
@@ -28,19 +26,15 @@ module Wallaby
28
26
  # @param query [ActiveRecord::Relation]
29
27
  # @param params [ActionController::Parameters]
30
28
  # @return [ActiveRecord::Relation] paginated query
31
- # @see Wallaby::ModelServiceProvider#paginate
32
29
  def paginate(query, params)
33
- # NOTE: do not take out the `.to_i` as will_paginate requires an integer `per_page`
34
30
  per = (params[:per] || Wallaby.configuration.pagination.page_size).to_i
35
- query = query.page params[:page] if query.respond_to? :page
36
- query = query.per per if query.respond_to? :per # kaminari
37
- query = query.per_page per if query.respond_to? :per_page # will_paginate
31
+ page = [params[:page].to_i, 1].max # starting from page 1
32
+ query = query.offset((page - 1) * per).limit(per)
38
33
  query
39
34
  end
40
35
 
41
36
  # @note No mass assignment happens here!
42
37
  # @return [Object] new resource object
43
- # @see Wallaby::ModelServiceProvider#new
44
38
  def new(_params, _authorizer)
45
39
  @model_class.new
46
40
  end
@@ -50,7 +44,6 @@ module Wallaby
50
44
  # @param id [Integer, String]
51
45
  # @return [Object] persisted resource object
52
46
  # @raise [Wallaby::ResourceNotFound] when record is not found
53
- # @see Wallaby::ModelServiceProvider#find
54
47
  def find(id, _params, _authorizer)
55
48
  @model_class.find id
56
49
  rescue ::ActiveRecord::RecordNotFound
@@ -61,7 +54,6 @@ module Wallaby
61
54
  # @param resource [Object]
62
55
  # @param params [ActionController::Parameters]
63
56
  # @param authorizer [Wallaby::ModelAuthorizer]
64
- # @see Wallaby::ModelServiceProvider#create
65
57
  def create(resource, params, authorizer)
66
58
  save __callee__, resource, params, authorizer
67
59
  end
@@ -70,14 +62,12 @@ module Wallaby
70
62
  # @param resource [Object]
71
63
  # @param params [ActionController::Parameters]
72
64
  # @param authorizer [Wallaby::ModelAuthorizer]
73
- # @see Wallaby::ModelServiceProvider#update
74
65
  def update(resource, params, authorizer)
75
66
  save __callee__, resource, params, authorizer
76
67
  end
77
68
 
78
69
  # Remove a record from database
79
70
  # @param resource [Object]
80
- # @see Wallaby::ModelServiceProvider#destroy
81
71
  def destroy(resource, _params, _authorizer)
82
72
  resource.destroy
83
73
  end
@@ -109,7 +99,8 @@ module Wallaby
109
99
 
110
100
  # See if a resource is valid
111
101
  # @param resource [Object]
112
- # @return [Boolean]
102
+ # @return [true] if valid
103
+ # @return [false] otherwise
113
104
  def valid?(resource)
114
105
  validator.valid? resource
115
106
  end
@@ -137,22 +128,22 @@ module Wallaby
137
128
  permitter.simple_field_names << permitter.compound_hashed_fields
138
129
  end
139
130
 
140
- # @see Wallaby::ModelServiceProvider::Permitter
131
+ # @return [Wallaby::ActiveRecord::ModelServiceProvider::Permitter]
141
132
  def permitter
142
133
  @permitter ||= Permitter.new @model_decorator
143
134
  end
144
135
 
145
- # @see Wallaby::ModelServiceProvider::Querier
136
+ # @return [Wallaby::ActiveRecord::ModelServiceProvider::Querier]
146
137
  def querier
147
138
  @querier ||= Querier.new @model_decorator
148
139
  end
149
140
 
150
- # @see Wallaby::ModelServiceProvider::Normalizer
141
+ # @return [Wallaby::ActiveRecord::ModelServiceProvider::Normalizer]
151
142
  def normalizer
152
143
  @normalizer ||= Normalizer.new @model_decorator
153
144
  end
154
145
 
155
- # @see Wallaby::ModelServiceProvider::Validator
146
+ # @return [Wallaby::ActiveRecord::ModelServiceProvider::Validator]
156
147
  def validator
157
148
  @validator ||= Validator.new @model_decorator
158
149
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Wallaby
4
4
  class ActiveRecord
5
- # Pundit provider for ActiveRecord
5
+ # Pundit provider for {Wallaby::ActiveRecord}
6
6
  class PunditProvider < PunditAuthorizationProvider
7
7
  # Filter a scope
8
8
  # @param _action [Symbol, String]
@@ -11,7 +11,7 @@ module Wallaby
11
11
  def accessible_for(_action, scope)
12
12
  Pundit.policy_scope! user, scope
13
13
  rescue Pundit::NotDefinedError
14
- Rails.logger.warn "Cannot find scope policy for #{scope.inspect}.\nfrom #{__FILE__}:#{__LINE__}"
14
+ Logger.warn "Cannot find scope policy for `#{scope}`."
15
15
  scope
16
16
  end
17
17
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wallaby
4
- # ActiveRecord mode
4
+ # The Wallaby::ActiveRecord mode that implements the {Wallaby::Core} interfaces
5
+ # to handle ActiveRecord model/instance in all CRUD operations
5
6
  class ActiveRecord < Mode
6
7
  end
7
8
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Wallaby
4
4
  module ActiveRecordGem
5
- VERSION = '0.2.2' # :nodoc:
5
+ VERSION = '0.2.7' # :nodoc:
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wallaby-active_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tian Chen
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-19 00:00:00.000000000 Z
11
+ date: 2022-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.2.0
33
+ version: 0.2.3
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.2.0
40
+ version: 0.2.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: cancancan
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -132,7 +132,7 @@ metadata:
132
132
  homepage_uri: https://github.com/wallaby-rails/wallaby-active_record
133
133
  source_code_uri: https://github.com/wallaby-rails/wallaby-active_record
134
134
  changelog_uri: https://github.com/wallaby-rails/wallaby-active_record/blob/master/CHANGELOG.md
135
- post_install_message:
135
+ post_install_message:
136
136
  rdoc_options: []
137
137
  require_paths:
138
138
  - lib
@@ -147,8 +147,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  - !ruby/object:Gem::Version
148
148
  version: '0'
149
149
  requirements: []
150
- rubygems_version: 3.0.3
151
- signing_key:
150
+ rubygems_version: 3.1.2
151
+ signing_key:
152
152
  specification_version: 4
153
153
  summary: Wallaby's ActiveRecord ORM adapter
154
154
  test_files: []