zip_search 0.1.3

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/javascripts/zip_search/init.js +16 -0
  6. data/app/assets/javascripts/zip_search/jquery.inputmask.bundle.js +2145 -0
  7. data/app/assets/javascripts/zip_search/locations.coffee +69 -0
  8. data/app/assets/stylesheets/zip_search/application.css +15 -0
  9. data/app/assets/stylesheets/zip_search/locations.css +4 -0
  10. data/app/assets/stylesheets/zip_search/typeahead.scss +72 -0
  11. data/app/controllers/zip_search/application_controller.rb +4 -0
  12. data/app/controllers/zip_search/locations_controller.rb +43 -0
  13. data/app/helpers/zip_search/application_helper.rb +4 -0
  14. data/app/helpers/zip_search/locations_helper.rb +4 -0
  15. data/app/models/zip_search/location.rb +9 -0
  16. data/app/views/layouts/zip_search/application.html.erb +14 -0
  17. data/app/views/zip_search/_simple_fields.html.erb +4 -0
  18. data/app/views/zip_search/locations/_search_field.html.erb +19 -0
  19. data/config/routes.rb +4 -0
  20. data/config/spring.rb +1 -0
  21. data/db/migrate/20150914014727_create_zip_search_locations.rb +17 -0
  22. data/db/migrate/20150920034901_add_zs_association_name_to_zip_search_locations.rb +7 -0
  23. data/lib/generators/zip_search/install/install_generator.rb +22 -0
  24. data/lib/tasks/zip_search_tasks.rake +4 -0
  25. data/lib/zip_search/acts_as_location.rb +61 -0
  26. data/lib/zip_search/capybara_helpers.rb +9 -0
  27. data/lib/zip_search/controller_helpers.rb +79 -0
  28. data/lib/zip_search/engine.rb +38 -0
  29. data/lib/zip_search/has_locations.rb +87 -0
  30. data/lib/zip_search/model_helpers.rb +29 -0
  31. data/lib/zip_search/railtie.rb +18 -0
  32. data/lib/zip_search/simple_form_helper.rb +50 -0
  33. data/lib/zip_search/strong_params_helper.rb +17 -0
  34. data/lib/zip_search/version.rb +3 -0
  35. data/lib/zip_search/view_helpers.rb +31 -0
  36. data/lib/zip_search.rb +62 -0
  37. data/spec/factories/travelers.rb +40 -0
  38. data/spec/features/travelers_spec.rb +68 -0
  39. data/spec/models/traveler_spec.rb +38 -0
  40. data/spec/models/zip_search/location_spec.rb +7 -0
  41. data/spec/spec_helper.rb +134 -0
  42. metadata +383 -0
@@ -0,0 +1,69 @@
1
+ $ ->
2
+ return unless (form=$('.location-search-form')).is('*')
3
+ $(document).ready ->
4
+ states = new Bloodhound
5
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name')
6
+ queryTokenizer: Bloodhound.tokenizers.whitespace
7
+ prefetch: '/zip_search/location_search.json?by=state&a=1'
8
+ cities = new Bloodhound
9
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name')
10
+ queryTokenizer: Bloodhound.tokenizers.whitespace
11
+ remote:
12
+ url: '/zip_search/location_search.json?by=city&q=%QUERY'
13
+ wildcard: '%QUERY'
14
+ counties = new Bloodhound
15
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name')
16
+ queryTokenizer: Bloodhound.tokenizers.whitespace
17
+ remote:
18
+ url: '/zip_search/location_search.json?by=county&q=%QUERY'
19
+ wildcard: '%QUERY'
20
+ zips = new Bloodhound
21
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name')
22
+ queryTokenizer: Bloodhound.tokenizers.whitespace
23
+ remote:
24
+ url: '/zip_search/location_search.json?by=zip&q=%QUERY'
25
+ wildcard: '%QUERY'
26
+ data_defaults =
27
+ display: 'name'
28
+ templates:
29
+ empty: '<span></span>'
30
+ suggestion: (datum)->
31
+ output = "<p style='line-height:16px;padding:5px 20px'>#{datum.name}"
32
+ output += "<br /><small>#{datum.desc}</small>" if datum.desc?
33
+ output += '</p>'
34
+ datasets = [
35
+ $.extend({}, data_defaults, name: 'zips', source: zips),
36
+ $.extend({}, data_defaults, name: 'counties', source: counties),
37
+ $.extend({}, data_defaults, name: 'cities', source: cities),
38
+ $.extend({}, data_defaults, name: 'states', source: states) ]
39
+ $('.zipsearch-typeahead').typeahead null, datasets
40
+ $('.zipsearch-typeahead').on 'typeahead:selected', (e, datum, name)=>
41
+ e.preventDefault()
42
+ console.log datum
43
+ ulid_attrs = type: 'hidden', name: 'ulid'
44
+ by_attrs = type: 'hidden', name: 'by'
45
+ form.prepend $('<input />').attr(ulid_attrs).val(datum.id)
46
+ form.prepend $('<input />').attr(by_attrs).val(datum.param)
47
+ form.submit()
48
+
49
+ $ ->
50
+ return unless $('.zs-location-fields').is('*')
51
+ $(document).ready ->
52
+ $('.zs-location-fields').each ->
53
+ zip_field = $(@).find('input[name*=zip]')
54
+ county_field = $(@).find('input[name*=county]')
55
+ city_field = $(@).find('input[name*=city]')
56
+ state_field = $(@).find('select[name*=state]')
57
+ zip_field.inputmask
58
+ mask: '99999[-9999]'
59
+ greedy: false
60
+ oncomplete: ->
61
+ v = zip_field.val()
62
+ $.post '/zip_search/zipcode_search', {q: v}, (data)->
63
+ console.log data
64
+ if data.status == 'ok'
65
+ county_field.val(data.county) if data.county?
66
+ city_field.val(data.city) if data.city?
67
+ state_field.find('option').filter(-> $(@).text()==data.state )
68
+ .prop('selected', true) if data.state?
69
+
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,72 @@
1
+ // Typeahead
2
+ .twitter-typeahead {
3
+ width:100%;
4
+ color: black;
5
+ }
6
+ .twitter-typeahead .tt-hint {
7
+ display: block;
8
+ height: 34px;
9
+ line-height: 1.428571429;
10
+ border: 1px solid transparent;
11
+ border-radius:4px;
12
+ .simple_form.checkin & {
13
+ padding: 6px 12px;
14
+ font-size: 13px;
15
+ }
16
+ }
17
+ .twitter-typeahead .hint-small {
18
+ height: 30px;
19
+ padding: 5px 10px;
20
+ font-size: 12px;
21
+ border-radius: 3px;
22
+ line-height: 1.5;
23
+ }
24
+ .twitter-typeahead .hint-large {
25
+ height: 45px;
26
+ padding: 10px 16px;
27
+ font-size: 18px;
28
+ border-radius: 6px;
29
+ line-height: 1.33;
30
+ }
31
+ .tt-menu {
32
+ z-index:1500;
33
+ padding: 8px 0px;
34
+ background-color: #ffffff;
35
+ border: 1px solid rgba(0,0,0,0.2);
36
+ border-radius: 0px 0px 8px 8px;
37
+ box-shadow: 0px 5px 10px rgba(0,0,0,0.2);
38
+ width:100%;
39
+ }
40
+ .tt-suggestion {
41
+ padding: 3px 20px;
42
+ line-height: 24px;
43
+ font-size:14px;
44
+ &.tt-cursor {
45
+ color: #ffffff;
46
+ background-color: #0097cf;
47
+ small { color: white; }
48
+ }
49
+ p {margin:0px;}
50
+ }
51
+ .typeahead-header {
52
+ margin:5px 10px;
53
+ padding:5px 10px;
54
+ font-weight: 100;
55
+ border-top: 1px solid #cccccc;
56
+ border-bottom: 1px solid #cccccc;
57
+ }
58
+
59
+ .location-search-form {
60
+ margin: 10px 6px 0 0;
61
+ width: 33%;
62
+ // @media(max-width: $screen-md-max) { width: 50%; }
63
+ // @media(max-width: $screen-sm-max) { width: 100%; }
64
+
65
+ .location-search-clear-icon {
66
+ position: relative;
67
+ top: -29px;
68
+ right: 5px;
69
+ float: right;
70
+ }
71
+ }
72
+
@@ -0,0 +1,4 @@
1
+ module ZipSearch
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,43 @@
1
+ require_dependency 'zip_search/application_controller'
2
+
3
+ module ZipSearch
4
+ class LocationsController < ApplicationController
5
+
6
+ def location_search
7
+ q = params[:q]; by = params[:by]
8
+ results = if by.present?
9
+ case by
10
+ when 'state'
11
+ US_STATES.map{|s| { name: s[0], id: s[1], param: by } }
12
+ when 'city'
13
+ search = Location.search_by_city q
14
+ search.to_a.uniq{|r| [ r[:city], r[:state] ] }.map{|r|
15
+ { id: r[:id], name: r[:city], param: by,
16
+ desc: "City in #{r[:state]}" } }
17
+ when 'county'
18
+ search = Location.search_by_county q
19
+ search.to_a.uniq{|r| [ r[:county], r[:state] ] }.map{|r|
20
+ { id: r[:id], name: r[:county], param: by,
21
+ desc: "County in #{r[:city]}, #{r[:state]}" } }
22
+ when 'zip'
23
+ search = Location.search_by_zip q
24
+ search.to_a.uniq(&:zip).map{|r|
25
+ { id: r[:id], name: r[:zip], param: by,
26
+ desc: "#{r[:county]}, #{r[:city]}, #{r[:state]}" } }
27
+ end
28
+ else [] end
29
+ render json: results
30
+ end
31
+
32
+ def zipcode_search
33
+ results = do_zipcode_search params[:q]
34
+ if results.any?
35
+ results[:status] ||= 'ok'
36
+ render json: results
37
+ else
38
+ render json: { status: 'not ok', error: 'No results' }
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,4 @@
1
+ module ZipSearch
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ZipSearch
2
+ module LocationsHelper
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ module ZipSearch
2
+ class Location < ActiveRecord::Base
3
+
4
+ acts_as_location
5
+
6
+ belongs_to :locatable, polymorphic: true
7
+
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>ZipSearch</title>
5
+ <%= stylesheet_link_tag "zip_search/application", media: "all" %>
6
+ <%= javascript_include_tag "zip_search/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,4 @@
1
+ <%= f.input :zip %>
2
+ <%= f.input :county %>
3
+ <%= f.input :city %>
4
+ <%= f.input :state, collection: ZipSearch::US_STATES %>
@@ -0,0 +1,19 @@
1
+ <% if defined? SimpleForm %>
2
+ <%= simple_form_for :location_search, settings[:form] do |f| %>
3
+ <%= f.input_field :q, settings[:field] %>
4
+ <% if params.key?(:location_search) %>
5
+ <%= link_to params.except(:location_search, :ulid, :by), class: 'location-search-clear-icon' do %>
6
+ <span class="glyphicon glyphicon-remove"></span>
7
+ <% end %>
8
+ <% end %>
9
+ <% end %>
10
+ <% else %>
11
+ <%= form_for :location_search, settings[:form] do |f| %>
12
+ <%= f.text_field :q, settings[:field] %>
13
+ <% if params.key?(:location_search) %>
14
+ <%= link_to params.except(:location_search, :ulid, :by), class: 'location-search-clear-icon' do %>
15
+ <span class="glyphicon glyphicon-remove"></span>
16
+ <% end %>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ ZipSearch::Engine.routes.draw do
2
+ get '/location_search' => 'locations#location_search', as: :location_search
3
+ post '/zipcode_search' => 'locations#zipcode_search', as: :zipcode_search
4
+ end
data/config/spring.rb ADDED
@@ -0,0 +1 @@
1
+ Spring.application_root = './test/dummy'
@@ -0,0 +1,17 @@
1
+ class CreateZipSearchLocations < ActiveRecord::Migration
2
+ def change
3
+ create_table :zip_search_locations do |t|
4
+ t.integer :locatable_id
5
+ t.string :locatable_type
6
+ t.string :title
7
+ t.string :zip
8
+ t.string :county
9
+ t.string :city
10
+ t.string :state
11
+ t.float :latitude
12
+ t.float :longitude
13
+
14
+ t.timestamps null: false
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ class AddZsAssociationNameToZipSearchLocations < ActiveRecord::Migration
2
+ def change
3
+ add_column :zip_search_locations, :zs_association_name, :string
4
+ add_index :zip_search_locations, :zs_association_name
5
+ add_index :zip_search_locations, [ :locatable_type, :locatable_id ]
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ require 'zip_search'
2
+
3
+ module ZipSearch
4
+ module Generators
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+
7
+ def inject_engine_routing
8
+ inject_into_file 'config/routes.rb', after: 'pplication.routes.draw do' do
9
+ "\n mount ZipSearch::Engine => '/zipsearch'\n"
10
+ end
11
+ end
12
+
13
+ def inject_js_initializer
14
+ inject_into_file 'app/assets/javascripts/application.js', before: '//= require_tree .' do
15
+ "//= require zip_search/init\n"
16
+ end
17
+ end
18
+
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :zip_search do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,61 @@
1
+ require 'zip_search'
2
+ require 'pg_search'
3
+
4
+ module ZipSearch
5
+ module ActsAsLocation
6
+ extend ActiveSupport::Concern
7
+ include PgSearch
8
+
9
+ included do
10
+ end
11
+
12
+ module ClassMethods
13
+ def acts_as_location(title=:location, options={})
14
+ pg_search_scope :search_by_location,
15
+ against: [:zip, :county, :city, :state],
16
+ using: {tsearch: {prefix: true}}
17
+ pg_search_scope :search_by_state, against: :state,
18
+ using: {tsearch: {prefix: true}}
19
+ pg_search_scope :search_by_city, against: :city,
20
+ using: {tsearch: {prefix: true}}
21
+ pg_search_scope :search_by_county, :against => :county,
22
+ using: {tsearch: {prefix: true}}
23
+ pg_search_scope :search_by_zip, against: :zip
24
+
25
+ geocoded_by :to_sentence
26
+ after_validation :geocode
27
+
28
+ validates_presence_of :zs_association_name
29
+
30
+ include ActsAsLocation::LocalInstanceMethods
31
+ end
32
+
33
+ def location_fields; %i( zip county city state ) end
34
+
35
+ def params(extra_params=[])
36
+ extra_params + [ :zs_association_name, :title, :latitude, :longitude,
37
+ :zip, :county, :city, :state ]
38
+ end
39
+
40
+ def nested_params(extra_params=[])
41
+ [:id, :_destroy] + params(extra_params)
42
+ end
43
+ end
44
+
45
+ module LocalInstanceMethods
46
+
47
+ def location_fields; self.class.location_fields end
48
+ def location; Hash[ location_fields.map{|lf| [ lf, send(lf) ] } ] end
49
+ def location_blank?; location.values.reject(&:blank?).empty? end
50
+
51
+ def to_sentence
52
+ return 'Location unavailable' if location_blank?
53
+ location_fields.map{|lf| send(lf) }.reject(&:blank?).join(', ')
54
+ end
55
+
56
+
57
+ end
58
+
59
+ end
60
+ end
61
+
@@ -0,0 +1,9 @@
1
+ # require 'zipsearch'
2
+ MFaker = defined?(Faker) ? Faker : FFaker
3
+
4
+ def random_zip; rand(10000..99999).to_s end
5
+
6
+ module ZipSearch
7
+ module CapybaraHelpers
8
+ end
9
+ end
@@ -0,0 +1,79 @@
1
+ require 'zip_search'
2
+ require 'zip_search/strong_params_helper'
3
+
4
+ module ZipSearch
5
+ module ControllerHelpers
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include StrongParamsHelper
10
+ end
11
+
12
+ def do_zipcode_search(zip)
13
+ Geocoder.configure(lookup: :nominatim)
14
+ n_results = Geocoder.search zip, bounds: US_BOUNDS
15
+ Geocoder.configure(lookup: :google)
16
+ g_results = Geocoder.search zip, bounds: US_BOUNDS
17
+ response = {}
18
+ if n_results.any? || g_results.any?
19
+ if g_results.any?
20
+ g_best_match = g_results.first
21
+ response[:city] = g_best_match.city
22
+ response[:state] = g_best_match.state
23
+ end
24
+ if n_results.any?
25
+ n_best_match = if g_results.any?
26
+ matching_g_results = n_results.select{|r|
27
+ (r.city.present? && r.city == g_best_match.city) ||
28
+ (r.state.present? && r.state == g_best_match.state) }
29
+ if matching_g_results.any? then
30
+ matching_g_results.first
31
+ else n_results.first end
32
+ else n_results.first end
33
+ response[:county] = n_best_match.county
34
+ response[:city] ||= n_best_match.city
35
+ response[:state] ||= n_best_match.state
36
+ end
37
+ end
38
+ response
39
+ end
40
+
41
+
42
+ def apply_location_filtering(target)
43
+ return target unless params.key? :zs_location
44
+ zl = Location.find_by(id: params[:zlid].to_i)
45
+ q = params[:location_search][:q]
46
+ zl_association = if has_zipsearch_locations? target
47
+ :zipsearch_locations
48
+ elsif has_zipsearch_location? target
49
+ :zipsearch_location
50
+ end
51
+ return target if zl_association.nil?
52
+ wq = case params[:by]
53
+ when 'zip' then ['zipsearch_locations.zip = ?', q]
54
+ when 'county'
55
+ ['zipsearch_locations.county = ? AND zipsearch_locations.state = ?',
56
+ zl.county, zl.state]
57
+ when 'city'
58
+ ['zipsearch_locations.city = ? AND zipsearch_locations.state = ?',
59
+ zl.city, zl.state]
60
+ when 'state' then ['zipsearch_locations.state = ?', q]
61
+ end
62
+ return target unless wq.present?
63
+ target.joins(zl_association).where(*wq)
64
+ end
65
+
66
+ protected
67
+ def has_zipsearch_locations?(klass, name=:zipsearch_locations)
68
+ klass.reflect_on_all_associations(:has_many)
69
+ .any?{|a| a.name == name }
70
+ end
71
+ def has_zipsearch_location?(klass, name=:zipsearch_location)
72
+ klass.reflect_on_all_associations(:has_one)
73
+ .any?{|a| a.name == name } ||
74
+ klass.reflect_on_all_associations(:belongs_to)
75
+ .any?{|a| a.name == name }
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,38 @@
1
+ require 'zip_search'
2
+
3
+ module ZipSearch
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace ZipSearch
6
+
7
+ require 'zip_search/controller_helpers'
8
+ require 'zip_search/model_helpers'
9
+ require 'zip_search/view_helpers'
10
+ require 'zip_search/simple_form_helper'
11
+
12
+ initializer 'zip_search.controller_helpers' do
13
+ ActionController::Base.send :include, ControllerHelpers
14
+ end
15
+ initializer 'zip_search.model_helpers' do
16
+ ActiveRecord::Base.send :include, ModelHelpers
17
+ end
18
+ initializer 'zip_search.view_helpers' do
19
+ ActionView::Base.send :include, ViewHelpers
20
+ if defined? SimpleForm
21
+ ActionView::Base.send :include, SimpleFormHelper
22
+ SimpleFormHelper::METHODS_TO_EXPORT.each do |m|
23
+ SimpleForm::FormBuilder.send :define_method, m do |*args, &block|
24
+ template.send m, self, *args, &block
25
+ end
26
+ end
27
+ end
28
+ end
29
+ initializer :append_migrations do |app|
30
+ unless app.root.to_s == root.to_s
31
+ config.paths['db/migrate'].expanded.each do |expanded_path|
32
+ app.config.paths['db/migrate'] << expanded_path
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,87 @@
1
+ require 'zip_search'
2
+
3
+ module ZipSearch
4
+ module HasLocations
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def has_location(title=:location, options={})
9
+ has_one title, -> { where zs_association_name: title.to_s },
10
+ as: :locatable, class_name: 'ZipSearch::Location'
11
+ initialize_location_ownership! title
12
+ end
13
+
14
+ def has_locations(title=:locations, options={})
15
+ has_many title, -> { where zs_association_name: title.to_s },
16
+ as: :locatable, class_name: 'ZipSearch::Location'
17
+ initialize_locations_ownership! title
18
+ end
19
+
20
+ def has_location_history(title=:location_history)
21
+ has_locations title
22
+ has_location :current_location
23
+ before_validation if: -> { current_location && current_location.changed? } do
24
+ historical_attrs = {}
25
+ current_location.changes.each{|k, (oldval, _)|
26
+ historical_attrs[k] = oldval unless oldval.blank? }
27
+ if historical_attrs.any?
28
+ self.location_history << Location.new( Hash[
29
+ current_location.changes.map{|k, (v, _)| [ k, v ] } ].merge(
30
+ zs_association_name: 'location_history') )
31
+ end
32
+ end
33
+ end
34
+
35
+ def zipsearch_associations
36
+ reflect_on_all_associations.select{|a| a.foreign_key == 'locatable_id' }
37
+ end
38
+ def zipsearch_association_names
39
+ zipsearch_associations.map(&:name) end
40
+ def zipsearch_association(title=nil)
41
+ zipsearch_associations.find{|za| title.nil? || za.name == title } end
42
+
43
+ def has_zipsearch_association?(title=nil)
44
+ return zipsearch_associations.any? if title.nil?
45
+ zipsearch_association_names.include?(title)
46
+ end
47
+
48
+ def initialize_location_ownership!(title=:location)
49
+ include HasLocations::LocalInstanceMethods
50
+ accepts_nested_attributes_for title, allow_destroy: true,
51
+ reject_if: :"should_reject_#{title}?"
52
+ before_validation :prune_empty_zs_locations
53
+ after_initialize if: :"should_build_#{title}?" do
54
+ send :"build_#{title}", zs_association_name: title end
55
+ end
56
+ def initialize_locations_ownership!(title=:locations)
57
+ include HasLocations::LocalInstanceMethods
58
+ accepts_nested_attributes_for title, allow_destroy: true,
59
+ reject_if: :"should_reject_#{title}?"
60
+ before_validation :prune_empty_zs_locations
61
+ unless title == :location_history
62
+ after_initialize if: :"should_build_#{title}?" do
63
+ send(title).build zs_association_name: title end
64
+ end
65
+ end
66
+ end
67
+
68
+ module LocalInstanceMethods
69
+
70
+ ZipSearch::ASSOCIATION_MODES.each do |am|
71
+ define_method :"should_build_#{am}?" do
72
+ respond_to?(am) && send(am).nil? end
73
+ define_method :"should_reject_#{am}?" do |attributed|
74
+ self.class.zipsearch_association(am).klass
75
+ .location_fields.select{|lf| !attributed[lf].blank? }.empty? end
76
+ end
77
+
78
+ def prune_empty_zs_locations
79
+ self.class.zipsearch_associations.each{|zsa|
80
+ Array(self.send(zsa.name)).each {|zsl|
81
+ zsl.delete if zsl.new_record? && zsl.location_blank? } }
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,29 @@
1
+ require 'zip_search'
2
+ require 'zip_search/acts_as_location'
3
+ require 'zip_search/has_locations'
4
+
5
+ module ZipSearch
6
+ module ModelHelpers
7
+ extend ActiveSupport::Concern
8
+
9
+ include ActsAsLocation
10
+ include HasLocations
11
+
12
+ # DENVER = [39.7643389, -104.8551114]
13
+ # def self.random_address
14
+ # loop do
15
+ # random_point = Geocoder::Calculations.random_point_near(DENVER, 30)
16
+ # query = Geocoder::Query.new(random_point)
17
+ # results = query.execute
18
+ # results.select! do |r|
19
+ # addr = r.data['formatted_address']
20
+ # addr.present? && !(addr =~ /Unnamed Road/) && r.data['address_components'].select{|c| c['types'].include?('street_number') }.any?
21
+ # end
22
+ # next unless results.any?
23
+ # break results[0]# .data['formatted_address']
24
+ # end
25
+ # end
26
+ #
27
+
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ require 'zip_search'
2
+
3
+ module ZipSearch
4
+ require 'rails'
5
+ class Railtie < Rails::Railtie
6
+ initializer 'zip_search.insert_into_active_record' do |app|
7
+ ActiveSupport.on_load :active_record do
8
+ ZipSearch::Railtie.insert
9
+ end
10
+ end
11
+
12
+ def self.insert
13
+ if defined?(ActiveRecord)
14
+ # ActiveRecord::Base.send(:include, ZipSearch::Glue)
15
+ end
16
+ end
17
+ end
18
+ end