rad_core_rails 0.5.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ceead2841d732a837cd792358174211cd357da39c2f72b1435fdd47a68cc354
4
- data.tar.gz: f3fc5c87d4e9a9769b13354b3b1f6594936f54d97221c56910e1d9a4c677eb07
3
+ metadata.gz: 797ce809d45ddaf19fb1814c794efa557aaeea953fc3738934cdd439bd03c2ac
4
+ data.tar.gz: bb5da6af47c850baff2f6b441f63fce2941c3abedfd6edb7466781bf7be6e82d
5
5
  SHA512:
6
- metadata.gz: 343d979caca6c5ef32a443720983111ef7c4b1f9d1f5bfe58517929ddba0d0c3dcafaa9f8d87b62f1a307259f3af2be829e78e9e9bbf942975bc8544cdfbe7e8
7
- data.tar.gz: 9b0205167f8736b088c68ab4eefba1aae2997480bd1fd4eb427924287985bbc0bbb7b4eb84e4633b1a596634d758674e779574b9a1e7a63b8484a4d667fb9665
6
+ metadata.gz: 1dfeb36f7c9ee7a7be931c744e2c52ed0c22a61120196b65e367413c68ad9a48f1898d4ab2a93ac1591d919b6226463e91d997a8ff637638c8340effa3eb571f
7
+ data.tar.gz: 9160fa81a7777235ddd45c9ecea26565ae1fc3ee798074547dc07f723374a0cfe850af678fdc2cd1e23c05824c9595f38e1359481c6a4bb600e2146e7c38171e
data/.gitignore CHANGED
@@ -8,9 +8,10 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  Gemfile.lock
11
- bin
11
+ /bin/
12
12
  *.gem
13
13
  .rspec_status
14
14
  .ruby-gemset
15
15
  .ruby-version
16
16
  /.byebug_history
17
+ .robocop.yml
@@ -0,0 +1,4 @@
1
+ AllCops:
2
+ SuggestExtensions: false
3
+ Style/FrozenStringLiteralComment:
4
+ Enabled: false
data/README.md CHANGED
@@ -1,44 +1,104 @@
1
- # RadCoreRails
2
-
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rad_core_rails`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
6
-
7
- ## Installation
8
-
9
- Add this line to your application's Gemfile:
10
-
11
- ```ruby
12
- gem 'rad_core_rails'
13
- ```
14
-
15
- And then execute:
16
-
17
- $ bundle install
18
-
19
- Or install it yourself as:
20
-
21
- $ gem install rad_core_rails
22
-
23
- ## Usage
24
-
25
- TODO: Write usage instructions here
26
-
27
- ## Development
28
-
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
-
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
-
33
- ## Contributing
34
-
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rad_core_rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/rad_core_rails/blob/master/CODE_OF_CONDUCT.md).
36
-
37
-
38
- ## License
39
-
40
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
41
-
42
- ## Code of Conduct
43
-
44
- Everyone interacting in the RadCoreRails project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/rad_core_rails/blob/master/CODE_OF_CONDUCT.md).
1
+ # RadCoreRails
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rad_core_rails`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'rad_core_rails'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install rad_core_rails
22
+
23
+ ## Usage
24
+
25
+ For basic functionality just include `RadCoreRails` to your `ActiveRecord` model.
26
+
27
+ ```ruby
28
+ class Job < ActiveRecord::Base
29
+ include RadCoreRails
30
+ ...
31
+ end
32
+ ```
33
+
34
+ For using Zip Codes by Radius filter follow the steps below:
35
+
36
+ Step 1 - generate migration:
37
+
38
+ ```shell script
39
+ rails generate rad_core_rails:zip_codes_migration
40
+ ```
41
+
42
+ Step 2 - migrate DB:
43
+
44
+ ```shell script
45
+ rails db:migrate
46
+ ```
47
+
48
+ Step 3 - import all US zip-codes to your DB:
49
+ > this gem has built in JSON file with all US zip-codes
50
+
51
+ ```shell script
52
+ rails rad_core_rails:import_zip_codes
53
+ ```
54
+
55
+ Step 4 - add new filter to `filter_manifest` method like on example:
56
+
57
+ > OLD Syntax
58
+ ```ruby
59
+ class Job < ActiveRecord::Base
60
+ include RadCoreRails
61
+
62
+ def self.filter_manifest
63
+ {
64
+ zip_codes: ->(filter) { generate_zip_codes_clauses('addresses.zip', filter) }
65
+ }.with_indifferent_access
66
+ end
67
+ end
68
+ ```
69
+
70
+ > NEW Syntax starting from v0.7.0
71
+
72
+ ```ruby
73
+ class Job < ActiveRecord::Base
74
+ include RadCoreRails
75
+
76
+ filterable zip_codes: [:zip_codes, 'addresses.zip']
77
+ end
78
+ ```
79
+
80
+ Step 5 - filter params example
81
+
82
+ ```ruby
83
+ { key: 'zip_codes', option: '15', values: ['85001'] }
84
+ ```
85
+
86
+
87
+ ## Development
88
+
89
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
90
+
91
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
92
+
93
+ ## Contributing
94
+
95
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rad_core_rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/rad_core_rails/blob/master/CODE_OF_CONDUCT.md).
96
+
97
+
98
+ ## License
99
+
100
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
101
+
102
+ ## Code of Conduct
103
+
104
+ Everyone interacting in the RadCoreRails project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/rad_core_rails/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,6 +1,11 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require './support/active_record_rake_tasks'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
9
+ # Stub the :environment task for tasks like db:migrate & db:seed. Since this is a Gem we've explicitly required all
10
+ # dependent files in the needed places and we don't have to load the entire environment.
11
+ task :environment
@@ -0,0 +1,17 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :zip_codes do |t|
4
+ t.string :city
5
+ t.string :state
6
+ t.string :zip
7
+ t.decimal :latitude, precision: 10, scale: 6
8
+ t.decimal :longitude, precision: 10, scale: 6
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :zip_codes, :zip, unique: true
14
+ add_index :zip_codes, :latitude, unique: true
15
+ add_index :zip_codes, :longitude, unique: true
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ require "rails/generators/active_record"
2
+
3
+ module RadCoreRails
4
+ module Generators
5
+ class ZipCodesMigrationGenerator < Rails::Generators::Base
6
+ include ActiveRecord::Generators::Migration
7
+ source_root File.join(__dir__, "templates")
8
+
9
+ def copy_migration
10
+ migration_template "migration.rb", "db/migrate/create_zip_codes.rb", migration_version: migration_version
11
+ end
12
+
13
+ def migration_version
14
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,15 +1,18 @@
1
- require "rad_core_rails/version"
2
- require "active_support/concern"
3
- require "active_support/core_ext"
1
+ # Need some refactoring: https://ankane.org/gem-patterns
4
2
  require "active_record"
5
- require 'rad_core_rails/query_generator'
6
- require 'rad_core_rails/search_terms'
7
- require 'rad_core_rails/sortable'
3
+
4
+ require "rad_core_rails/railtie" if defined?(Rails)
5
+ require "rad_core_rails/version"
6
+ require "rad_core_rails/query_generator"
7
+ require "rad_core_rails/search_terms"
8
+ require "rad_core_rails/sortable"
8
9
 
9
10
  module RadCoreRails
11
+ autoload :ZipCode, 'rad_core_rails/zip_code'
12
+
10
13
  def self.included(receiver)
11
14
  receiver.send :include, Sortable, SearchTerms, QueryGenerator
12
15
  end
13
16
 
14
17
  class Error < StandardError; end
15
- end
18
+ end
@@ -11,13 +11,17 @@ module RadCoreRails
11
11
  @searchable_columns = handle_columns(columns)
12
12
  end
13
13
 
14
+ def filterable(filters_data = [])
15
+ @filter_manifest = handle_filterable(filters_data)
16
+ end
17
+
14
18
  def joins_clause
15
19
  <<-SQL
16
20
  SQL
17
21
  end
18
22
 
19
23
  def filter_manifest
20
- {}.with_indifferent_access
24
+ @filter_manifest || {}.with_indifferent_access
21
25
  end
22
26
 
23
27
  def create_filters(search, filters)
@@ -29,28 +33,102 @@ module RadCoreRails
29
33
  args << arg
30
34
  end
31
35
  filters.map do |filter|
32
- clause, arguments = filter_manifest[filter[:key]].call(filter)
33
- query << clause
34
- arguments.each do |arg|
35
- args << arg
36
+ begin
37
+ clause, arguments = filter_manifest[filter[:key]].call(filter)
38
+ query << clause
39
+ arguments.each do |arg|
40
+ args << arg
41
+ end
42
+ rescue NoMethodError
43
+ raise NoMethodError.new("Filter with the name `#{filter[:key]}` doesn't exist.")
36
44
  end
37
45
  end
38
46
  [query.reject(&:blank?).join(' AND '), args]
39
47
  end
40
48
 
49
+ # def generate_search_clause(search)
50
+ # and_args = []
51
+ # or_args = []
52
+ # if search.present? && searchable_columns.is_a?(Array)
53
+ # # search_term_size = search.split(' ').length
54
+ # and_terms = search.split(' ').select { |term| !term.include?('+') }
55
+ # or_terms = search.split(' ').select { |term| term.include?('+') }
56
+ # and_columns = []
57
+ # or_columns = []
58
+ # # just_ors = search_term_size == or_term_size
59
+ # # operand = just_ors ? 'OR' : 'AND'
60
+ # # search_terms = just_ors == true ? search.split(' ') : search.split(' ').select { |term| !term.include?('+') }
61
+ # and_terms.each do |term|
62
+ # columns = []
63
+ # and_terms.each do |col|
64
+ # and_columns.push("(LOWER(#{col}) ILIKE ?)")
65
+ # and_args.push '%' + term.downcase.strip + '%'
66
+ # end
67
+ # or_terms.each do |col|
68
+ # columns.push("(LOWER(#{col}) ILIKE ?)")
69
+ # or_args.push '%' + term[1, term.length].downcase.strip + '%'
70
+ # end
71
+ # clause = if or_columns.empty?
72
+ # '(' + and_columns.join(' OR ') + ')'
73
+ # else
74
+ # '(' + '(' + and_columns.join(' OR ') + ')' + 'AND' + '(' + or_columns.join(' OR ') + ')' + ')'
75
+ # end
76
+ # end
77
+ # [clause, and_args + or_args]
78
+ # else
79
+ # ['', []]
80
+ # end
81
+ # end
82
+
83
+ # @searchable_columns, is an array of column names that you can compare to your terms.
84
+ #
41
85
  def generate_search_clause(search)
42
- args = []
43
86
  if search.present? && searchable_columns.is_a?(Array)
44
- clause = []
45
- search.split(' ').each do |term|
87
+ # holds all of the sql from sanitized_and_terms
88
+ and_args = []
89
+ # holds all the table columns sql for all the AND terms
90
+ and_clause = []
91
+ # holds all of the sql from sanitized_or_terms
92
+ or_args = []
93
+ # holds all the table columns sql for all the OR terms
94
+ or_clause = []
95
+ # extract and terms, remove casing, and add ILIKE '%' comparisions
96
+ sanitized_and_terms = search.split(' ')
97
+ .reject { |term| term.include?('+') }
98
+ .map { |term| '%' + term.downcase.strip + '%' }
99
+ # extract or terms, remove casing, and add ILIKE '%' comparisions
100
+ sanitized_or_terms = search.split(' ')
101
+ .select { |term| term.include?('+') }
102
+ .map { |term| '%' + term[1, term.length].downcase.strip + '%' }
103
+ # loop through sanitized_and_terms to find all possible columns.
104
+ sanitized_and_terms.each do |sanitized_term|
46
105
  columns = []
106
+ # all possible columns where this should be searched
47
107
  searchable_columns.each do |col|
48
108
  columns.push("(LOWER(#{col}) ILIKE ?)")
49
- args.push '%' + term.downcase.strip + '%'
109
+ and_args.push sanitized_term
50
110
  end
51
- clause.push '(' + columns.join(' OR ') + ')'
111
+ and_clause.push '(' + columns.join(' OR ') + ')'
112
+ end
113
+ # loop through sanitized_or_terms to find all possible columns.
114
+ sanitized_or_terms.each do |sanitized_term|
115
+ columns = []
116
+ # all possible columns where this should be searched
117
+ searchable_columns.each do |col|
118
+ columns.push("(LOWER(#{col}) ILIKE ?)")
119
+ or_args.push sanitized_term
120
+ end
121
+ or_clause.push '(' + columns.join(' OR ') + ')'
122
+ end
123
+ if or_clause.empty? && and_clause.empty?
124
+ ['', []]
125
+ elsif or_clause.empty?
126
+ ['(' + and_clause.join(' AND ') + ')', and_args]
127
+ elsif and_clause.empty?
128
+ ['(' + or_clause.join(' OR ') + ')', or_args]
129
+ else
130
+ ['((' + and_clause.join(' AND ') + ') AND (' + or_clause.join(' OR ') + '))', and_args + or_args]
52
131
  end
53
- ['(' + clause.join(' AND ') + ')', args]
54
132
  else
55
133
  ['', []]
56
134
  end
@@ -65,13 +143,26 @@ module RadCoreRails
65
143
  [str, args]
66
144
  end
67
145
 
68
- def generate_model_clause(column_name, filter)
69
- str = "(#{column_name} = ANY(ARRAY[?]))"
146
+ def generate_model_clause(column_name, filter, optional_exclusion_clause = nil)
147
+ str = if optional_exclusion_clause.present? && filter[:option] == '!='
148
+ optional_exclusion_clause
149
+ else
150
+ # probably should fix this.
151
+ # somtimes we arent sending a filter option, so just default it to =
152
+ "(#{column_name} #{filter[:option] || '='} ANY(ARRAY[?]))"
153
+ end
70
154
  args = [filter[:values]]
71
155
 
72
156
  [str, args]
73
157
  end
74
158
 
159
+ def generate_zip_codes_clause(column_name, filter)
160
+ str = "(#{column_name} = ANY(#{zip_code_class.distance_query}))"
161
+ args = zip_code_class.distance_args(filter)
162
+
163
+ [str, args]
164
+ end
165
+
75
166
  def generate_array_clause(column_name, filter)
76
167
  str = "(#{column_name} && ARRAY[?])"
77
168
  args = [filter[:values]]
@@ -148,9 +239,7 @@ module RadCoreRails
148
239
  private
149
240
 
150
241
  def handle_columns(columns)
151
- if columns.empty?
152
- return self.columns.select { |c| c.type == :string }.map { |c| "#{table_name}.#{c.name}" }
153
- end
242
+ return self.columns.select { |c| c.type == :string }.map { |c| "#{table_name}.#{c.name}" } if columns.empty?
154
243
 
155
244
  columns.map do |c|
156
245
  splitted_c = c.split('.')
@@ -162,6 +251,27 @@ module RadCoreRails
162
251
  end
163
252
  end
164
253
  end
254
+
255
+ def handle_filterable(filters_data)
256
+ return nil if filters_data.empty?
257
+ @zip_code_class = filters_data.delete(:zip_code_class)
258
+
259
+ result = {}
260
+
261
+ filters_data.each do |filter_data|
262
+ filter_type, filter_column = filter_data.last
263
+ result[filter_data.first] = lambda { |filter|
264
+ public_send("generate_#{filter_type}_clause", filter_column, filter)
265
+ }
266
+ end
267
+
268
+ result.with_indifferent_access
269
+ end
270
+
271
+ def zip_code_class
272
+ return RadCoreRails::ZipCode unless @zip_code_class
273
+ @zip_code_class.to_s.constantize
274
+ end
165
275
  end
166
276
  end
167
277
  end