active_hash_relation 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.travis.yml +15 -0
  4. data/README.md +68 -11
  5. data/active_hash_relation.gemspec +13 -0
  6. data/lib/active_hash_relation.rb +18 -12
  7. data/lib/active_hash_relation/aggregation.rb +3 -0
  8. data/lib/active_hash_relation/association_filters.rb +3 -0
  9. data/lib/active_hash_relation/column_filters.rb +45 -11
  10. data/lib/active_hash_relation/filter_applier.rb +37 -23
  11. data/lib/active_hash_relation/helpers.rb +22 -2
  12. data/lib/active_hash_relation/scope_filters.rb +8 -1
  13. data/lib/active_hash_relation/sort_filters.rb +12 -0
  14. data/lib/active_hash_relation/version.rb +1 -1
  15. data/lib/active_record/scope_names.rb +4 -19
  16. data/lib/generators/active_hash_relation/initialize_generator.rb +13 -0
  17. data/lib/generators/active_hash_relation/templates/active_hash_relation.rb +12 -0
  18. data/spec/config/initializers/active_hash_relation.rb +16 -0
  19. data/spec/db/migrate/001_basic_schema.rb +53 -0
  20. data/spec/dummy-rails4/.gitignore +13 -0
  21. data/spec/dummy-rails4/.ruby-version +1 -0
  22. data/spec/dummy-rails4/Gemfile +18 -0
  23. data/spec/dummy-rails4/README.md +3 -0
  24. data/spec/dummy-rails4/Rakefile +6 -0
  25. data/spec/dummy-rails4/app/controllers/application_controller.rb +5 -0
  26. data/spec/dummy-rails4/app/models/.keep +0 -0
  27. data/spec/dummy-rails4/app/models/address.rb +1 -0
  28. data/spec/dummy-rails4/app/models/micropost.rb +1 -0
  29. data/spec/dummy-rails4/app/models/relationship.rb +1 -0
  30. data/spec/dummy-rails4/app/models/user.rb +1 -0
  31. data/spec/dummy-rails4/bin/bundle +3 -0
  32. data/spec/dummy-rails4/bin/rails +4 -0
  33. data/spec/dummy-rails4/bin/rake +4 -0
  34. data/spec/dummy-rails4/bin/setup +29 -0
  35. data/spec/dummy-rails4/config.ru +4 -0
  36. data/spec/dummy-rails4/config/application.rb +35 -0
  37. data/spec/dummy-rails4/config/boot.rb +3 -0
  38. data/spec/dummy-rails4/config/database.yml +18 -0
  39. data/spec/dummy-rails4/config/environment.rb +5 -0
  40. data/spec/dummy-rails4/config/environments/development.rb +41 -0
  41. data/spec/dummy-rails4/config/environments/production.rb +79 -0
  42. data/spec/dummy-rails4/config/environments/test.rb +42 -0
  43. data/spec/dummy-rails4/config/initializers/active_hash_relation.rb +3 -0
  44. data/spec/dummy-rails4/config/initializers/assets.rb +11 -0
  45. data/spec/dummy-rails4/config/initializers/backtrace_silencers.rb +7 -0
  46. data/spec/dummy-rails4/config/initializers/cookies_serializer.rb +3 -0
  47. data/spec/dummy-rails4/config/initializers/filter_parameter_logging.rb +4 -0
  48. data/spec/dummy-rails4/config/initializers/inflections.rb +16 -0
  49. data/spec/dummy-rails4/config/initializers/mime_types.rb +4 -0
  50. data/spec/dummy-rails4/config/initializers/session_store.rb +3 -0
  51. data/spec/dummy-rails4/config/initializers/wrap_parameters.rb +14 -0
  52. data/spec/dummy-rails4/config/locales/en.yml +23 -0
  53. data/spec/dummy-rails4/config/routes.rb +56 -0
  54. data/spec/dummy-rails4/config/secrets.yml +22 -0
  55. data/spec/dummy-rails4/db/migrate/001_basic_schema.rb +1 -0
  56. data/spec/dummy-rails4/db/schema.rb +69 -0
  57. data/spec/dummy-rails4/spec/factories/factories.rb +1 -0
  58. data/spec/dummy-rails4/spec/rails_helper.rb +19 -0
  59. data/spec/dummy-rails4/spec/spec_helper.rb +99 -0
  60. data/spec/dummy-rails4/spec/support/support.rb +1 -0
  61. data/spec/dummy-rails4/spec/tests/tests_spec.rb +3 -0
  62. data/spec/dummy-rails5/.gitignore +17 -0
  63. data/spec/dummy-rails5/.rspec +2 -0
  64. data/spec/dummy-rails5/.ruby-version +1 -0
  65. data/spec/dummy-rails5/Gemfile +19 -0
  66. data/spec/dummy-rails5/README.md +3 -0
  67. data/spec/dummy-rails5/Rakefile +6 -0
  68. data/spec/dummy-rails5/app/controllers/application_controller.rb +3 -0
  69. data/spec/dummy-rails5/app/models/address.rb +1 -0
  70. data/spec/dummy-rails5/app/models/micropost.rb +1 -0
  71. data/spec/dummy-rails5/app/models/relationship.rb +1 -0
  72. data/spec/dummy-rails5/app/models/user.rb +1 -0
  73. data/spec/dummy-rails5/bin/bundle +3 -0
  74. data/spec/dummy-rails5/bin/rails +4 -0
  75. data/spec/dummy-rails5/bin/rake +4 -0
  76. data/spec/dummy-rails5/bin/setup +34 -0
  77. data/spec/dummy-rails5/bin/update +29 -0
  78. data/spec/dummy-rails5/config.ru +5 -0
  79. data/spec/dummy-rails5/config/application.rb +25 -0
  80. data/spec/dummy-rails5/config/boot.rb +3 -0
  81. data/spec/dummy-rails5/config/cable.yml +9 -0
  82. data/spec/dummy-rails5/config/database.yml +18 -0
  83. data/spec/dummy-rails5/config/environment.rb +5 -0
  84. data/spec/dummy-rails5/config/environments/development.rb +54 -0
  85. data/spec/dummy-rails5/config/environments/production.rb +86 -0
  86. data/spec/dummy-rails5/config/environments/test.rb +42 -0
  87. data/spec/dummy-rails5/config/initializers/active_hash_relation.rb +3 -0
  88. data/spec/dummy-rails5/config/initializers/application_controller_renderer.rb +6 -0
  89. data/spec/dummy-rails5/config/initializers/assets.rb +11 -0
  90. data/spec/dummy-rails5/config/initializers/backtrace_silencers.rb +7 -0
  91. data/spec/dummy-rails5/config/initializers/cookies_serializer.rb +5 -0
  92. data/spec/dummy-rails5/config/initializers/filter_parameter_logging.rb +4 -0
  93. data/spec/dummy-rails5/config/initializers/inflections.rb +16 -0
  94. data/spec/dummy-rails5/config/initializers/mime_types.rb +4 -0
  95. data/spec/dummy-rails5/config/initializers/new_framework_defaults.rb +24 -0
  96. data/spec/dummy-rails5/config/initializers/session_store.rb +3 -0
  97. data/spec/dummy-rails5/config/initializers/wrap_parameters.rb +14 -0
  98. data/spec/dummy-rails5/config/locales/en.yml +23 -0
  99. data/spec/dummy-rails5/config/puma.rb +47 -0
  100. data/spec/dummy-rails5/config/routes.rb +3 -0
  101. data/spec/dummy-rails5/config/secrets.yml +22 -0
  102. data/spec/dummy-rails5/config/spring.rb +6 -0
  103. data/spec/dummy-rails5/db/migrate/001_basic_schema.rb +2 -0
  104. data/spec/dummy-rails5/db/schema.rb +64 -0
  105. data/spec/dummy-rails5/spec/factories/factories.rb +1 -0
  106. data/spec/dummy-rails5/spec/rails_helper.rb +19 -0
  107. data/spec/dummy-rails5/spec/spec_helper.rb +99 -0
  108. data/spec/dummy-rails5/spec/support/support.rb +1 -0
  109. data/spec/dummy-rails5/spec/tests/tests_spec.rb +3 -0
  110. data/spec/factories/factories.rb +39 -0
  111. data/spec/models/address.rb +7 -0
  112. data/spec/models/application_record.rb +3 -0
  113. data/spec/models/micropost.rb +9 -0
  114. data/spec/models/relationship.rb +9 -0
  115. data/spec/models/user.rb +32 -0
  116. data/spec/support/support.rb +62 -0
  117. data/spec/tests/aggregations/avg_spec.rb +45 -0
  118. data/spec/tests/aggregations/max_spec.rb +45 -0
  119. data/spec/tests/aggregations/min_spec.rb +46 -0
  120. data/spec/tests/aggregations/sum_spec.rb +43 -0
  121. data/spec/tests/associations/belongs_to_spec.rb +96 -0
  122. data/spec/tests/associations/has_many_spec.rb +91 -0
  123. data/spec/tests/associations/has_one_spec.rb +89 -0
  124. data/spec/tests/booleans_spec.rb +29 -0
  125. data/spec/tests/limit_spec.rb +30 -0
  126. data/spec/tests/null_spec.rb +44 -0
  127. data/spec/tests/numbers_spec.rb +142 -0
  128. data/spec/tests/or_filter_spec.rb +98 -0
  129. data/spec/tests/primary_key_spec.rb +29 -0
  130. data/spec/tests/scopes_spec.rb +48 -0
  131. data/spec/tests/sorting_spec.rb +100 -0
  132. data/spec/tests/strings_spec.rb +86 -0
  133. metadata +392 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fd3f17dc4f8f76b811c90453a566ff8356baf769
4
- data.tar.gz: 3a2d2ed0091644d391fc8402879220fbd936c624
3
+ metadata.gz: a59228314ab4ecac89c7ba2f988514e453d2b434
4
+ data.tar.gz: 16316aba61f2ff203189607d69fd9e738649187d
5
5
  SHA512:
6
- metadata.gz: f37a7bd401b3c5609d125ffdd669bdf41c75fb5b8b8e5e8f879ec9c5dacf66088afb692dfb3ca1e994927be2375a2a08867b7078b59203985fb90ebfe0da3441
7
- data.tar.gz: e749fe442aef147137ef82991b06f70a62cf600762c7f6be420696736fcc824b26f1830197a847bda7c9b4cdac90db5b713ca42013c6304fd1fb10d7d66e1cf1
6
+ metadata.gz: 67342eb6d946bea38b57f1b3e1f99f389c815fef0f6c89445c806ccde2750b18b33810d8cf60ac7b982281de08ad3564f329147d9d6b18be31070345a64f445e
7
+ data.tar.gz: aa938f3c84a0054be00d9af1fe80cda17ef4a9e0f249bd4d91a23d065230d3052cb7f8da81a215171d54e4226c959bc0fe7cefad328be807ea0977f914e27998
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm: 2.3.3
4
+ cache:
5
+ bundler: true
6
+ install:
7
+ - cd spec/dummy-rails4 && bundle install
8
+ - cd ../dummy-rails5 && bundle install
9
+ script:
10
+ - echo $(pwd)
11
+ - cd ../dummy-rails4 && bundle exec rspec spec/
12
+ - echo $(pwd)
13
+ - cd ../dummy-rails5 && bundle exec rspec spec/
14
+ notifications:
15
+ email: false
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # ActiveHashRelation
2
+ [ ![Codeship Status for kollegorna/active_hash_relation](https://app.codeship.com/projects/02f08850-cc66-0134-028f-5ad72e690a75/status?branch=master)](https://app.codeship.com/projects/200194)
2
3
 
3
4
  ## Introduction
4
5
  Simple gem that allows you to manipulate ActiveRecord::Relation using JSON. For instance:
@@ -11,7 +12,7 @@ apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9}
11
12
  ```
12
13
  or even filter a resource based on it's associations' associations:
13
14
  ```ruby
14
- apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9, areas: {id: 22} }})
15
+ apply_filters(resource, {updated_at: { geq: "2014-11-2 14:25:04"}, unit: {id: 9, areas: {or: [{id: 22}, {id: 21}]} }})
15
16
  ```
16
17
  and the list could go on.. Basically your whole db is exposed\* there. It's perfect for filtering a collection of resources on APIs.
17
18
 
@@ -29,7 +30,7 @@ which might or might not be a security issue. If you don't like that check
29
30
 
30
31
  Add this line to your application's Gemfile:
31
32
 
32
- gem 'active_hash_relation'
33
+ gem 'active_hash_relation', '~> 1.2.0
33
34
 
34
35
  And then execute:
35
36
 
@@ -38,8 +39,10 @@ And then execute:
38
39
  Or install it yourself as:
39
40
 
40
41
  $ gem install active_hash_relation
42
+
41
43
  ## How to use
42
- The gem exposes only one method: `apply_filters(resource, hash_params, include_associations: true, model: nil)`. `resource` is expected to be an ActiveRecord::Relation.
44
+ The gem exposes only one method: `apply_filters(resource, hash_params, include_associations: true, model: nil)`.
45
+ `resource` is expected to be an ActiveRecord::Relation.
43
46
  That way, you can add your custom filters before passing the `Relation` to `ActiveHashRelation`.
44
47
 
45
48
  In order to use it you have to include ActiveHashRelation module in your class. For instance in a Rails API controller you would do:
@@ -58,21 +61,40 @@ class Api::V1::ResourceController < Api::V1::BaseController
58
61
  end
59
62
  ```
60
63
 
64
+ If you **need to enable filtering on scopes**, you need to specify that explicitly from the initializer. For instance:
65
+
66
+ ```ruby
67
+ ActiveHashRelation.configure do |config|
68
+ #override default scope when accessing associations
69
+ config.use_unscoped = true
70
+ #set true to be able to filter scopes (with params)
71
+ #please note that unfortunately (:/) rails does not provide any way
72
+ #to iterate through scopes so it uses a monkey patch.
73
+ #The monkey patch is as gentle as it can be by aliasing the method, adds some
74
+ #sugar and calls it,
75
+ #You need to run `initialize!` to actually include the required files
76
+ config.filter_active_record_scopes = true
77
+ end
78
+
79
+ #requires monkeyparched scopes, optional if you don't enable them
80
+ ActiveHashRelation.initialize!
81
+ ```
82
+
61
83
  ## The API
62
84
  ### Columns
63
- For each param, `apply_filters` method will search in the model's (derived from the first param, or explicitly defined as the last param) all the record's column names and associations. (filtering based on scopes are not working at the moment but will be supported soon). For each column, if there is such a param, it will apply the filter based on the column type. The following column types are supported:
85
+ For each param, `apply_filters` method will search in the model's (derived from the
86
+ first param, or explicitly defined as the last param) all the record's column names
87
+ and associations. (filtering based on scopes are not working at the moment but
88
+ will be supported soon). For each column, if there is such a param, it will
89
+ apply the filter based on the column type. The following column types are supported:
64
90
 
65
- #### Primary
66
- You can apply a filter a column which is a primary key by value or using an array like:
67
- * `{primary_key_column: 5}`
68
- * `{primary_key_column: [1,3,4,5,6,7]}`
69
91
 
70
92
  #### Integer, Float, Decimal, Date, Time or Datetime/Timestamp
71
93
  You can apply an equality filter:
72
94
 
73
95
  * `{example_column: 500}`
74
96
 
75
- or using an array
97
+ or using an array (`ActiveRecord` translates that internally to an `IN` query)
76
98
  * `{example_column: [500, 40]}`
77
99
 
78
100
  or using a hash as a value you get more options:
@@ -100,7 +122,13 @@ or using a hash as a value you get more options:
100
122
  * `{example_column: {eq: 'exact value'}}` `#runs: EXAMPLE_COLUMN = 'test'`
101
123
  * `{example_column: {starts_with: 'exac'}}` `#runs: EXAMPLE_COLUMN LIKE 'test%'`
102
124
  * `{example_column: {ends_with: 'alue'}}` `#runs: EXAMPLE_COLUMN LIKE '%test'`
103
- * `{example_column: {like: 'ct_va}}` `#runs: EXAMPLE_COLUMN LIKE '%test%'`
125
+ * `{example_column: {like: 'ct_va'}}` `#runs: EXAMPLE_COLUMN LIKE '%test%'`
126
+
127
+ If you want to filter using `ILIKE` you can pass an `with_ilike` param:
128
+
129
+ * `{example_column: {like: 'ct_va', with_ilike: true}}` `#runs: EXAMPLE_COLUMN ILIKE '%test%'`
130
+ * `{example_column: {like: 'ct_va', with_ilike: true}}` `#runs: EXAMPLE_COLUMN ILIKE '%test%'`
131
+
104
132
 
105
133
  **Please note that ILIKE and especially LIKE are quite slow if you have millions of records in the db even with an index.**
106
134
 
@@ -129,12 +157,41 @@ micropost_filter = Micropost.all.where("CREATED_AT =< ?", '12-9-2014'.to_datetim
129
157
  User.where(email: 'test@user.com').joins(:microposts).merge(micropost_filter)
130
158
  ```
131
159
 
160
+ ### NULL Filter
161
+ You can apply null filter for generate query like this `"users.name IS NULL"` or `"users.name IS NOT NULL"` with this following code:
162
+ `{ name: { null: true } }` for is null filter and `{ name: { null: false } }` for not null filter.
163
+
164
+ this can be used also for relations tables, so you can write like this `{ books: {title: {null: false }} }`
165
+
166
+
167
+ ### OR Filter
168
+ You can apply an SQL `OR` (for ActiveRecord 5+) using the following syntax:
169
+ `{or: [{name: 'Filippos'}, {name: 'Vasilis'}]}`
170
+
171
+ It will generate: `WHERE ((users.name = 'Filippos') OR (users.name = 'Vasilis'))`
172
+
173
+ You can apply an `OR` on associations as well or even nested ones, there isn't much limitation on that.
174
+ I suggest you though to take a look on the [tests](spec/tests/or_filter_spec.rb), cause the syntax gets a bit complex after a while ;)
175
+
176
+
132
177
  ### Scopes
133
- Scopes are supported via a tiny monkeypatch in the ActiveRecord's scope class method which holds the name of each scope. Only scopes that don't accept arguments are supported. The rest could also be supported but it wouldn't make much sense.. If you want to filter based on a scope in a model, the scope names should go under `scopes` sub-hash. For instance the following:
178
+ **Filtering on scopes is not enabled by default**.
179
+
180
+ Scopes are supported via a tiny monkeypatch in the ActiveRecord's scope class method which holds the name of each scope.
181
+ The monkey patch is as gentle as it can be: it aliases the method, adds some sugar and executes it.
182
+
183
+ Scopes with arguments are also supported but not tested much. Probably they will work fine unless your arguments expect
184
+ complex objects.
185
+
186
+ If you want to filter based on a scope in a model, the scope names should go under `scopes` sub-hash. For instance the following:
134
187
  * `{ scopes: { planned: true } }`
135
188
 
136
189
  will run the `.planned` scope on the resource.
137
190
 
191
+ * `{scopes: {created_between: [1988, 2018]}}`
192
+
193
+ will run the `.created_on(1988, 2018)` scope on the resource.
194
+
138
195
  ### Unscoped assotiations
139
196
  If you have a default scope in your models and you have a good reason to keep that, `active_hash_relation` provides an option to override it when filtering associations:
140
197
 
@@ -18,6 +18,19 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_runtime_dependency "activerecord"
22
+
23
+ spec.add_development_dependency "activerecord"
21
24
  spec.add_development_dependency "bundler", "~> 1.6"
22
25
  spec.add_development_dependency "rake", "~> 10.0"
26
+
27
+ spec.add_development_dependency 'rspec'
28
+ spec.add_development_dependency "factory_girl_rails", "~> 4.0"
29
+ spec.add_development_dependency 'faker'
30
+ spec.add_development_dependency 'database_cleaner'
31
+ spec.add_development_dependency 'pry'
32
+ spec.add_development_dependency 'sqlite3'
33
+ spec.add_development_dependency 'pg'
34
+ spec.add_development_dependency 'rails'
35
+ spec.add_development_dependency 'rspec-rails'
23
36
  end
@@ -1,14 +1,13 @@
1
- require "active_record/scope_names"
2
- require "active_hash_relation/version"
3
- require "active_hash_relation/helpers"
4
- require "active_hash_relation/column_filters"
5
- require "active_hash_relation/scope_filters"
6
- require "active_hash_relation/sort_filters"
7
- require "active_hash_relation/limit_filters"
8
- require "active_hash_relation/association_filters"
9
- require "active_hash_relation/filter_applier"
10
-
11
- require "active_hash_relation/aggregation"
1
+ require_relative "active_hash_relation/version"
2
+ require_relative "active_hash_relation/helpers"
3
+ require_relative "active_hash_relation/column_filters"
4
+ require_relative "active_hash_relation/scope_filters"
5
+ require_relative "active_hash_relation/sort_filters"
6
+ require_relative "active_hash_relation/limit_filters"
7
+ require_relative "active_hash_relation/association_filters"
8
+ require_relative "active_hash_relation/filter_applier"
9
+
10
+ require_relative "active_hash_relation/aggregation"
12
11
 
13
12
  module ActiveHashRelation
14
13
  class << self
@@ -23,6 +22,13 @@ module ActiveHashRelation
23
22
  def self.configuration
24
23
  @configuration ||= Configuration.new do
25
24
  self.has_filter_classes = false
25
+ self.filter_active_record_scopes = false
26
+ end
27
+ end
28
+
29
+ def self.initialize!
30
+ if self.configuration.filter_active_record_scopes
31
+ require_relative "active_record/scope_names"
26
32
  end
27
33
  end
28
34
 
@@ -41,6 +47,6 @@ module ActiveHashRelation
41
47
 
42
48
  class Configuration
43
49
  attr_accessor :has_filter_classes, :filter_class_prefix, :filter_class_suffix,
44
- :use_unscoped
50
+ :use_unscoped, :filter_active_record_scopes
45
51
  end
46
52
  end
@@ -12,6 +12,9 @@ module ActiveHashRelation
12
12
 
13
13
  unless @model
14
14
  @model = model_class_name(@resource)
15
+ if @model.nil? || engine_name == @model.to_s
16
+ @model = model_class_name(@resource, true)
17
+ end
15
18
  end
16
19
  end
17
20
 
@@ -2,6 +2,9 @@ module ActiveHashRelation::AssociationFilters
2
2
  def filter_associations(resource, params, model = nil)
3
3
  unless model
4
4
  model = model_class_name(resource)
5
+ if model.nil? || engine_name == model.to_s
6
+ model = model_class_name(resource, true)
7
+ end
5
8
  end
6
9
 
7
10
  model.reflect_on_all_associations.map(&:name).each do |association|
@@ -1,14 +1,14 @@
1
1
  module ActiveHashRelation::ColumnFilters
2
- def filter_primary(resource, column, param)
3
- resource = resource.where(id: param)
4
- end
5
-
6
2
  def filter_integer(resource, column, table_name, param)
7
3
  if param.is_a? Array
8
4
  n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
9
5
  return resource.where("#{table_name}.#{column} IN (#{n_param})")
10
6
  elsif param.is_a? Hash
11
- return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
7
+ if !param[:null].nil?
8
+ return null_filters(resource, table_name, column, param)
9
+ else
10
+ return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
11
+ end
12
12
  else
13
13
  return resource.where("#{table_name}.#{column} = ?", param)
14
14
  end
@@ -27,7 +27,11 @@ module ActiveHashRelation::ColumnFilters
27
27
  n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
28
28
  return resource.where("#{table_name}.#{column} IN (#{n_param})")
29
29
  elsif param.is_a? Hash
30
- return apply_like_filters(resource, table_name, column, param)
30
+ if !param[:null].nil?
31
+ return null_filters(resource, table_name, column, param)
32
+ else
33
+ return apply_like_filters(resource, table_name, column, param)
34
+ end
31
35
  else
32
36
  return resource.where("#{table_name}.#{column} = ?", param)
33
37
  end
@@ -42,7 +46,11 @@ module ActiveHashRelation::ColumnFilters
42
46
  n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
43
47
  return resource.where("#{table_name}.#{column} IN (#{n_param})")
44
48
  elsif param.is_a? Hash
45
- return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
49
+ if !param[:null].nil?
50
+ return null_filters(resource, table_name, column, param)
51
+ else
52
+ return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
53
+ end
46
54
  else
47
55
  resource = resource.where(column => param)
48
56
  end
@@ -55,7 +63,11 @@ module ActiveHashRelation::ColumnFilters
55
63
  n_param = param.to_s.gsub("\"","'").gsub("[","").gsub("]","") #fix this!
56
64
  return resource = resource.where("#{table_name}.#{column} IN (#{n_param})")
57
65
  elsif param.is_a? Hash
58
- return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
66
+ if !param[:null].nil?
67
+ return null_filters(resource, table_name, column, param)
68
+ else
69
+ return apply_leq_geq_le_ge_filters(resource, table_name, column, param)
70
+ end
59
71
  else
60
72
  resource = resource.where(column => param)
61
73
  end
@@ -63,15 +75,25 @@ module ActiveHashRelation::ColumnFilters
63
75
  return resource
64
76
  end
65
77
 
66
- def filter_boolean(resource, column, param)
67
- b_param = ActiveRecord::Type::Boolean.new.type_cast_from_database(param)
78
+ def filter_boolean(resource, column, table_name, param)
79
+ if param.is_a?(Hash) && !param[:null].nil?
80
+ return null_filters(resource, table_name, column, param)
81
+ else
82
+ if ActiveRecord::VERSION::MAJOR >= 5
83
+ b_param = ActiveRecord::Type::Boolean.new.cast(param)
84
+ else
85
+ b_param = ActiveRecord::Type::Boolean.new.type_cast_from_database(param)
86
+ end
68
87
 
69
- resource = resource.where(column => b_param)
88
+ resource = resource.where(column => b_param)
89
+ end
70
90
  end
71
91
 
72
92
  private
73
93
 
74
94
  def apply_leq_geq_le_ge_filters(resource, table_name, column, param)
95
+ return resource.where("#{table_name}.#{column} = ?", param[:eq]) if param[:eq]
96
+
75
97
  if !param[:leq].blank?
76
98
  resource = resource.where("#{table_name}.#{column} <= ?", param[:leq])
77
99
  elsif !param[:le].blank?
@@ -109,4 +131,16 @@ module ActiveHashRelation::ColumnFilters
109
131
 
110
132
  return resource
111
133
  end
134
+
135
+ def null_filters(resource, table_name, column, param)
136
+ if param[:null] == true
137
+ resource = resource.where("#{table_name}.#{column} IS NULL")
138
+ end
139
+
140
+ if param[:null] == false
141
+ resource = resource.where("#{table_name}.#{column} IS NOT NULL")
142
+ end
143
+
144
+ return resource
145
+ end
112
146
  end
@@ -9,36 +9,26 @@ module ActiveHashRelation
9
9
 
10
10
  attr_reader :configuration
11
11
 
12
- def initialize(resource, params, include_associations: true, model: nil)
12
+ def initialize(resource, params, include_associations: false, model: nil)
13
13
  @configuration = Module.nesting.last.configuration
14
14
  @resource = resource
15
- @params = HashWithIndifferentAccess.new(params)
15
+ if params.respond_to?(:to_unsafe_h)
16
+ @params = HashWithIndifferentAccess.new(params.to_unsafe_h)
17
+ else
18
+ @params = HashWithIndifferentAccess.new(params)
19
+ end
16
20
  @include_associations = include_associations
17
- @model = model
21
+ @model = find_model(model)
18
22
  end
19
23
 
20
-
21
24
  def apply_filters
22
- unless @model
23
- @model = model_class_name(@resource)
24
- end
25
+ run_or_filters
26
+
25
27
  table_name = @model.table_name
26
28
  @model.columns.each do |c|
27
29
  next if @params[c.name.to_s].nil?
28
30
  next if @params[c.name.to_s].is_a?(String) && @params[c.name.to_s].blank?
29
31
 
30
- if c.respond_to?(:primary)
31
- if c.primary
32
- @resource = filter_primary(@resource, c.name, @params[c.name])
33
- next
34
- end
35
- else #rails 4.2
36
- if @model.primary_key == c.name
37
- @resource = filter_primary(@resource, c.name, @params[c.name])
38
- next
39
- end
40
- end
41
-
42
32
  case c.type
43
33
  when :integer
44
34
  @resource = filter_integer(@resource, c.name, table_name, @params[c.name])
@@ -46,19 +36,24 @@ module ActiveHashRelation
46
36
  @resource = filter_float(@resource, c.name, table_name, @params[c.name])
47
37
  when :decimal
48
38
  @resource = filter_decimal(@resource, c.name, table_name, @params[c.name])
49
- when :string
39
+ when :string, :uuid, :text
50
40
  @resource = filter_string(@resource, c.name, table_name, @params[c.name])
51
41
  when :date
52
42
  @resource = filter_date(@resource, c.name, table_name, @params[c.name])
53
43
  when :datetime, :timestamp
54
44
  @resource = filter_datetime(@resource, c.name, table_name, @params[c.name])
55
45
  when :boolean
56
- @resource = filter_boolean(@resource, c.name, @params[c.name])
46
+ @resource = filter_boolean(@resource, c.name, table_name, @params[c.name])
57
47
  end
58
48
  end
59
49
 
60
-
61
- @resource = filter_scopes(@resource, @params[:scopes], @model) if @params.include?(:scopes)
50
+ if @params.include?(:scopes)
51
+ if ActiveHashRelation.configuration.filter_active_record_scopes
52
+ @resource = filter_scopes(@resource, @params[:scopes], @model)
53
+ else
54
+ Rails.logger.warn('Ignoring ActiveRecord scope filters because they are not enabled')
55
+ end
56
+ end
62
57
  @resource = filter_associations(@resource, @params, @model) if @include_associations
63
58
  @resource = apply_limit(@resource, @params[:limit]) if @params.include?(:limit)
64
59
  @resource = apply_sort(@resource, @params[:sort], @model) if @params.include?(:sort)
@@ -69,5 +64,24 @@ module ActiveHashRelation
69
64
  def filter_class(resource_name)
70
65
  "#{configuration.filter_class_prefix}#{resource_name.pluralize}#{configuration.filter_class_suffix}".constantize
71
66
  end
67
+
68
+ def run_or_filters
69
+ if @params[:or].is_a?(Array)
70
+ if ActiveRecord::VERSION::MAJOR < 5
71
+ return Rails.logger.warn("OR query is supported on ActiveRecord 5+")
72
+ end
73
+
74
+ if @params[:or].length >= 2
75
+ array = @params[:or].map do |or_param|
76
+ self.class.new(@resource, or_param, include_associations: @include_associations).apply_filters
77
+ end
78
+
79
+ @resource = @resource.merge(array[0])
80
+ array[1..-1].each{|query| @resource = @resource.or(query)}
81
+ else
82
+ Rails.logger.warn("Can't run an OR with 1 element!")
83
+ end
84
+ end
85
+ end
72
86
  end
73
87
  end