geo_labels 0.2.0
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 +7 -0
 - data/CHANGELOG.md +15 -0
 - data/MIT-LICENSE +20 -0
 - data/README.md +28 -0
 - data/Rakefile +8 -0
 - data/app/assets/config/geo_labels_manifest.js +2 -0
 - data/app/assets/javascripts/geo_labels/application.coffee +29 -0
 - data/app/assets/stylesheets/geo_labels/application.sass +10 -0
 - data/app/controllers/concerns/geo_labels/shared_template_and_instance_methods.rb +21 -0
 - data/app/controllers/geo_labels/application_controller.rb +14 -0
 - data/app/controllers/geo_labels/contacts_controller.rb +56 -0
 - data/app/controllers/geo_labels/dashboard_controller.rb +33 -0
 - data/app/controllers/geo_labels/labels_controller.rb +56 -0
 - data/app/helpers/geo_labels/application_helper.rb +197 -0
 - data/app/jobs/geo_labels/application_job.rb +4 -0
 - data/app/mailers/geo_labels/application_mailer.rb +6 -0
 - data/app/models/geo_labels/application_record.rb +5 -0
 - data/app/models/geo_labels/contact.rb +64 -0
 - data/app/models/geo_labels/contact_label.rb +40 -0
 - data/app/models/geo_labels/label.rb +15 -0
 - data/app/views/geo_labels/application/_button-destroy-record.html.slim +1 -0
 - data/app/views/geo_labels/application/_button-edit-record.html.slim +1 -0
 - data/app/views/geo_labels/application/_button-new-record.html.slim +1 -0
 - data/app/views/geo_labels/application/_messages.html.slim +4 -0
 - data/app/views/geo_labels/application/_navigation.html.slim +29 -0
 - data/app/views/geo_labels/contacts/_form.html.slim +46 -0
 - data/app/views/geo_labels/contacts/edit.html.slim +3 -0
 - data/app/views/geo_labels/contacts/index.html.slim +31 -0
 - data/app/views/geo_labels/contacts/new.html.slim +3 -0
 - data/app/views/geo_labels/contacts/show.html.slim +20 -0
 - data/app/views/geo_labels/dashboard/export.html.slim +4 -0
 - data/app/views/geo_labels/dashboard/import.html.slim +7 -0
 - data/app/views/geo_labels/dashboard/main.html.slim +35 -0
 - data/app/views/geo_labels/labels/_form.html.slim +13 -0
 - data/app/views/geo_labels/labels/edit.html.slim +3 -0
 - data/app/views/geo_labels/labels/index.html.slim +47 -0
 - data/app/views/geo_labels/labels/new.html.slim +3 -0
 - data/app/views/geo_labels/labels/show.html.slim +12 -0
 - data/app/views/layouts/geo_labels/application.html.slim +22 -0
 - data/config/environment.rb +2 -0
 - data/config/initializers/human_plural.rb +16 -0
 - data/config/locales/geo_labels.en.yml +5 -0
 - data/config/locales/geo_labels.es.yml +5 -0
 - data/config/routes.rb +13 -0
 - data/db/migrate/20221019150722_create_geo_labels_contacts.rb +20 -0
 - data/db/migrate/20221020180213_create_geo_labels_labels.rb +16 -0
 - data/db/migrate/20221020195346_create_geo_labels_contact_labels.rb +12 -0
 - data/lib/geo_labels/engine.rb +36 -0
 - data/lib/geo_labels/exporter.rb +48 -0
 - data/lib/geo_labels/importer.rb +124 -0
 - data/lib/geo_labels/version.rb +3 -0
 - data/lib/geo_labels.rb +8 -0
 - data/lib/tasks/geo_labels_tasks.rake +4 -0
 - metadata +267 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 5bf986ec7c563cd8c2d4a80b0c47ba754995828ebdf137095504f0cd483cd31a
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: b67e43e78fcfc7990d6e0517de6fe13e2911ff018544b33d5af3c24d90099578
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 44b8b346ca204d88afab5edeac9cd65e07490f49b8b394b873295df151182092573af0573ef290a2da67b31fa0be8d83cfcd78bb91da7adfe2fb1a70d4205250
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: cc33f0f8be30c3baf48092763cb11b26d6b6d6429a4c944512e3b9a1d1e393a220230b8801c47eb942d089327eac5a5f657856448f237474664a2734bc6a465f
         
     | 
    
        data/CHANGELOG.md
    ADDED
    
    | 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            CHANGELOG of geo\_labels
         
     | 
| 
      
 2 
     | 
    
         
            +
            ============================
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            2023-02-10 v0.2.0
         
     | 
| 
      
 5 
     | 
    
         
            +
            -------------------------
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Add importer link to menu
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Add importer view
         
     | 
| 
      
 8 
     | 
    
         
            +
            * Add import file action
         
     | 
| 
      
 9 
     | 
    
         
            +
            * Add importer spec
         
     | 
| 
      
 10 
     | 
    
         
            +
            * Add exporter spec
         
     | 
| 
      
 11 
     | 
    
         
            +
            * Better labels tree on it\'s index page
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            2022-10-19 Initial setup
         
     | 
| 
      
 14 
     | 
    
         
            +
            -------------------------
         
     | 
| 
      
 15 
     | 
    
         
            +
            The base code
         
     | 
    
        data/MIT-LICENSE
    ADDED
    
    | 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Copyright 2022 Benjamin ter Kuile
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 4 
     | 
    
         
            +
            a copy of this software and associated documentation files (the
         
     | 
| 
      
 5 
     | 
    
         
            +
            "Software"), to deal in the Software without restriction, including
         
     | 
| 
      
 6 
     | 
    
         
            +
            without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 7 
     | 
    
         
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 8 
     | 
    
         
            +
            permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 9 
     | 
    
         
            +
            the following conditions:
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be
         
     | 
| 
      
 12 
     | 
    
         
            +
            included in all copies or substantial portions of the Software.
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 15 
     | 
    
         
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 16 
     | 
    
         
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
      
 17 
     | 
    
         
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
      
 18 
     | 
    
         
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
      
 19 
     | 
    
         
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
      
 20 
     | 
    
         
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
            Short description and motivation.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            ## Usage
         
     | 
| 
      
 5 
     | 
    
         
            +
            How to use my plugin.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            ## Installation
         
     | 
| 
      
 8 
     | 
    
         
            +
            Add this line to your application's Gemfile:
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 11 
     | 
    
         
            +
            gem "geo_labels"
         
     | 
| 
      
 12 
     | 
    
         
            +
            ```
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            And then execute:
         
     | 
| 
      
 15 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 16 
     | 
    
         
            +
            $ bundle
         
     | 
| 
      
 17 
     | 
    
         
            +
            ```
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            Or install it yourself as:
         
     | 
| 
      
 20 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 21 
     | 
    
         
            +
            $ gem install geo_labels
         
     | 
| 
      
 22 
     | 
    
         
            +
            ```
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            ## Contributing
         
     | 
| 
      
 25 
     | 
    
         
            +
            Contribution directions go here.
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            ## License
         
     | 
| 
      
 28 
     | 
    
         
            +
            The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
         
     | 
    
        data/Rakefile
    ADDED
    
    
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #= require jquery
         
     | 
| 
      
 2 
     | 
    
         
            +
            #= require jquery_ujs
         
     | 
| 
      
 3 
     | 
    
         
            +
            #= require semantic-ui
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            window.initMap = ->
         
     | 
| 
      
 6 
     | 
    
         
            +
              if map_canvas = document.getElementById('map-canvas')
         
     | 
| 
      
 7 
     | 
    
         
            +
                window.google_map = new google.maps.Map map_canvas,
         
     | 
| 
      
 8 
     | 
    
         
            +
                  center: map_points[0]
         
     | 
| 
      
 9 
     | 
    
         
            +
                  zoom: 9
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                map_points.forEach (map_point) ->
         
     | 
| 
      
 12 
     | 
    
         
            +
                  marker = new google.maps.Marker
         
     | 
| 
      
 13 
     | 
    
         
            +
                    position: map_point
         
     | 
| 
      
 14 
     | 
    
         
            +
                    map: google_map
         
     | 
| 
      
 15 
     | 
    
         
            +
                    title: map_point.title
         
     | 
| 
      
 16 
     | 
    
         
            +
                  false
         
     | 
| 
      
 17 
     | 
    
         
            +
            $ ->
         
     | 
| 
      
 18 
     | 
    
         
            +
              $('.ui.dropdown').dropdown() # assume fomantic that has the clearable class option, a lot better than the below custom code
         
     | 
| 
      
 19 
     | 
    
         
            +
              $('.message .close').on 'click', ->
         
     | 
| 
      
 20 
     | 
    
         
            +
                $(@).closest('.message').transition('fade')
         
     | 
| 
      
 21 
     | 
    
         
            +
              $('.accordion').accordion()
         
     | 
| 
      
 22 
     | 
    
         
            +
              $('.and-or-switch-buttons .button').on 'click', ->
         
     | 
| 
      
 23 
     | 
    
         
            +
                #debugger
         
     | 
| 
      
 24 
     | 
    
         
            +
                container = @parentNode
         
     | 
| 
      
 25 
     | 
    
         
            +
                button.classList.remove('secondary') for button in container.getElementsByClassName('button')
         
     | 
| 
      
 26 
     | 
    
         
            +
                @classList.add('secondary')
         
     | 
| 
      
 27 
     | 
    
         
            +
                container.querySelector('input').value = @dataset.predicate
         
     | 
| 
      
 28 
     | 
    
         
            +
                #$(@).find('button').removeClass ''
         
     | 
| 
      
 29 
     | 
    
         
            +
                false
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
              module SharedTemplateAndInstanceMethods
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                included do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  helper_method :record_class
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                private
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def record_class
         
     | 
| 
      
 12 
     | 
    
         
            +
                  result = controller_path.classify.safe_constantize
         
     | 
| 
      
 13 
     | 
    
         
            +
                  raise "Cannot determine record_class from controller_path: #{controller_path}" unless result
         
     | 
| 
      
 14 
     | 
    
         
            +
                  result
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def record_params
         
     | 
| 
      
 18 
     | 
    
         
            +
                  params.require(record_class.name.demodulize.underscore).permit!
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
              class ContactsController < ApplicationController
         
     | 
| 
      
 3 
     | 
    
         
            +
                include SharedTemplateAndInstanceMethods
         
     | 
| 
      
 4 
     | 
    
         
            +
                respond_to :html
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def index
         
     | 
| 
      
 7 
     | 
    
         
            +
                  authorize! :index, record_class
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @q = record_class.ransack query
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @q.sorts = 'name asc' if @q.sorts.empty?
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @records = @q.result.page(params[:page]).per(100)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  respond_with(@records)
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def new
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @record = record_class.new
         
     | 
| 
      
 16 
     | 
    
         
            +
                  authorize! :create, @record
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def create
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @record = record_class.new record_params
         
     | 
| 
      
 21 
     | 
    
         
            +
                  authorize! :create, @record
         
     | 
| 
      
 22 
     | 
    
         
            +
                  if @record.save
         
     | 
| 
      
 23 
     | 
    
         
            +
                    redirect_to @record, status: :see_other
         
     | 
| 
      
 24 
     | 
    
         
            +
                  else
         
     | 
| 
      
 25 
     | 
    
         
            +
                    render action: 'new'
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def show
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @record = record_class.find params[:id]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  authorize! :show, @record
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def edit
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @record = record_class.find(params[:id])
         
     | 
| 
      
 36 
     | 
    
         
            +
                  authorize! :edit, @record
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def update
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @record = record_class.find params[:id]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  authorize! :edit, @record
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if @record.update record_params
         
     | 
| 
      
 43 
     | 
    
         
            +
                    redirect_to @record, status: :see_other
         
     | 
| 
      
 44 
     | 
    
         
            +
                  else
         
     | 
| 
      
 45 
     | 
    
         
            +
                    render action: 'edit'
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def destroy
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @record = record_class.find params[:id]
         
     | 
| 
      
 51 
     | 
    
         
            +
                  authorize! :destroy, @record
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @record.destroy
         
     | 
| 
      
 53 
     | 
    
         
            +
                  redirect_to record_class, status: :see_other
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
              class DashboardController < ApplicationController
         
     | 
| 
      
 3 
     | 
    
         
            +
                def main
         
     | 
| 
      
 4 
     | 
    
         
            +
                  if params[:label_ids].present?
         
     | 
| 
      
 5 
     | 
    
         
            +
                    label_ids = params[:label_ids]
         
     | 
| 
      
 6 
     | 
    
         
            +
                    # fomantic-ui uses comma separated id values
         
     | 
| 
      
 7 
     | 
    
         
            +
                    label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @contacts = Contact.geocoded.for_label_ids(label_ids, predication: params[:predication])
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def export
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def export_file
         
     | 
| 
      
 16 
     | 
    
         
            +
                  timestamp = Time.now.utc.iso8601.first(19).gsub(/-|:/, '')
         
     | 
| 
      
 17 
     | 
    
         
            +
                  send_data GeoLabels::Exporter.export_str,
         
     | 
| 
      
 18 
     | 
    
         
            +
                    filename: "GeoLabels-export-#{timestamp}.yml",
         
     | 
| 
      
 19 
     | 
    
         
            +
                    type: :text,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    disposition: :attachment
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def import
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def import_file
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Importer.import params[:file]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  redirect_to root_path, notice: 'File imported'
         
     | 
| 
      
 29 
     | 
    
         
            +
                rescue => e
         
     | 
| 
      
 30 
     | 
    
         
            +
                  redirect_to import_path, alert: e.message
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
              class LabelsController < ApplicationController
         
     | 
| 
      
 3 
     | 
    
         
            +
                include SharedTemplateAndInstanceMethods
         
     | 
| 
      
 4 
     | 
    
         
            +
                respond_to :html
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def index
         
     | 
| 
      
 7 
     | 
    
         
            +
                  authorize! :index, record_class
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @q = record_class.ransack query
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @q.sorts = 'name asc' if @q.sorts.empty?
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @records = @q.result.page(params[:page]).per(100)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  respond_with(@records)
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def new
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @record = record_class.new
         
     | 
| 
      
 16 
     | 
    
         
            +
                  authorize! :create, @record
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def create
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @record = record_class.new record_params
         
     | 
| 
      
 21 
     | 
    
         
            +
                  authorize! :create, @record
         
     | 
| 
      
 22 
     | 
    
         
            +
                  if @record.save
         
     | 
| 
      
 23 
     | 
    
         
            +
                    redirect_to @record, status: :see_other
         
     | 
| 
      
 24 
     | 
    
         
            +
                  else
         
     | 
| 
      
 25 
     | 
    
         
            +
                    render action: 'new'
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def show
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @record = record_class.find params[:id]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  authorize! :show, @record
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def edit
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @record = record_class.find(params[:id])
         
     | 
| 
      
 36 
     | 
    
         
            +
                  authorize! :edit, @record
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def update
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @record = record_class.find params[:id]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  authorize! :edit, @record
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if @record.update record_params
         
     | 
| 
      
 43 
     | 
    
         
            +
                    redirect_to @record, status: :see_other
         
     | 
| 
      
 44 
     | 
    
         
            +
                  else
         
     | 
| 
      
 45 
     | 
    
         
            +
                    render action: 'edit'
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def destroy
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @record = record_class.find params[:id]
         
     | 
| 
      
 51 
     | 
    
         
            +
                  authorize! :destroy, @record
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @record.destroy
         
     | 
| 
      
 53 
     | 
    
         
            +
                  redirect_to record_class, status: :see_other
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,197 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
              module ApplicationHelper
         
     | 
| 
      
 3 
     | 
    
         
            +
                def link_to_labels_tree
         
     | 
| 
      
 4 
     | 
    
         
            +
                  #output = "<ul class='ui list'>"
         
     | 
| 
      
 5 
     | 
    
         
            +
                  output = tag.ul(class: 'ui list')[0..-6]
         
     | 
| 
      
 6 
     | 
    
         
            +
                  add_punctuation = '*'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  tree_formatter = Proc.new do |items, lead_space|
         
     | 
| 
      
 8 
     | 
    
         
            +
                    items.each do |item|
         
     | 
| 
      
 9 
     | 
    
         
            +
                      output += "#{lead_space}#{tag.li link_to(item[:name], label_path(item[:id]))}\n".html_safe
         
     | 
| 
      
 10 
     | 
    
         
            +
                      if item[:children].present?
         
     | 
| 
      
 11 
     | 
    
         
            +
                        output += "#{lead_space}<ul>".html_safe
         
     | 
| 
      
 12 
     | 
    
         
            +
                        tree_formatter.call(item[:children], lead_space + '  ')
         
     | 
| 
      
 13 
     | 
    
         
            +
                        output += "#{lead_space}</ul>".html_safe
         
     | 
| 
      
 14 
     | 
    
         
            +
                      end
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  tree_formatter.call(GeoLabels::Exporter.labels_tree_h, '')
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #output.gsub!(/^(\s*)  /, "\1#{add_punctuation} ") if add_punctuation.present?
         
     | 
| 
      
 19 
     | 
    
         
            +
                  output += '</ul>'.html_safe
         
     | 
| 
      
 20 
     | 
    
         
            +
                  output.html_safe
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
                # Return active or nil based on the given route spec
         
     | 
| 
      
 23 
     | 
    
         
            +
                #   %li{ class: active_class('users') # true if controller is users, false otherwise
         
     | 
| 
      
 24 
     | 
    
         
            +
                #   %li{ class: active_class('pages#about') # true if controller is pages and action is about
         
     | 
| 
      
 25 
     | 
    
         
            +
                #NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
      
 26 
     | 
    
         
            +
                def active_class(*route_specs)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  options = route_specs.extract_options!
         
     | 
| 
      
 28 
     | 
    
         
            +
                  return nil if Array.wrap(options[:except]).any?{|exception| current_route_spec?(exception) }
         
     | 
| 
      
 29 
     | 
    
         
            +
                  return 'active' if route_specs.any?{|rs| current_route_spec?(rs, options) }
         
     | 
| 
      
 30 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                # Check if the current route matches the route given as argument.
         
     | 
| 
      
 34 
     | 
    
         
            +
                # The syntax is meant to be a bit similar to specifying routes in
         
     | 
| 
      
 35 
     | 
    
         
            +
                # `config/routes.rb`.
         
     | 
| 
      
 36 
     | 
    
         
            +
                #   current_route_spec?('products') #=> true if controller name is products, false otherwise
         
     | 
| 
      
 37 
     | 
    
         
            +
                #   current_route_spec?('products#show') #=> true if controller_name is products AND action_name is show
         
     | 
| 
      
 38 
     | 
    
         
            +
                #   current_route_spec?('#show') #=> true if action_name is show, false otherwise
         
     | 
| 
      
 39 
     | 
    
         
            +
                #NOTE: this helper is tested through the active_class helper
         
     | 
| 
      
 40 
     | 
    
         
            +
                #NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
      
 41 
     | 
    
         
            +
                def current_route_spec?(route_spec, options = {})
         
     | 
| 
      
 42 
     | 
    
         
            +
                  return route_spec.match([controller_path, action_name].join('#')) if route_spec.is_a?(Regexp)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  controller, action = route_spec.split('#')
         
     | 
| 
      
 44 
     | 
    
         
            +
                  return action == params[:id] if controller_path == 'high_voltage/pages'
         
     | 
| 
      
 45 
     | 
    
         
            +
                  actual_controller_parts = controller_path.split('/')
         
     | 
| 
      
 46 
     | 
    
         
            +
                  if controller #and controller_path == controller
         
     | 
| 
      
 47 
     | 
    
         
            +
                    tested_controller_parts = controller.split('/')
         
     | 
| 
      
 48 
     | 
    
         
            +
                    return if tested_controller_parts.size > actual_controller_parts.size
         
     | 
| 
      
 49 
     | 
    
         
            +
                    if actual_controller_parts[0...tested_controller_parts.size] == tested_controller_parts
         
     | 
| 
      
 50 
     | 
    
         
            +
                      # controller spec matches
         
     | 
| 
      
 51 
     | 
    
         
            +
                      return true unless action
         
     | 
| 
      
 52 
     | 
    
         
            +
                      action_name == action
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  else
         
     | 
| 
      
 55 
     | 
    
         
            +
                    action_name == action
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                #NOTE: Taken from the dunlop-core gem. That is the best maintained version
         
     | 
| 
      
 60 
     | 
    
         
            +
                def page_title(*args, &blk)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  extra_content = capture(&blk) if blk.present?
         
     | 
| 
      
 62 
     | 
    
         
            +
                  if resource_title?(args)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    @scope_model = args[1].is_a?(ActiveRecord::Base) ? args[1].class : args[1]
         
     | 
| 
      
 64 
     | 
    
         
            +
                    content = page_title_for_resource(args)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  else
         
     | 
| 
      
 66 
     | 
    
         
            +
                    content = page_title_for_string(args)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
                  content += extra_content if extra_content.present?
         
     | 
| 
      
 69 
     | 
    
         
            +
                  title_tag = content_tag(:h2, content, class: 'page-title ui header')
         
     | 
| 
      
 70 
     | 
    
         
            +
                  content_for :page_title, title_tag
         
     | 
| 
      
 71 
     | 
    
         
            +
                  title_tag
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                def resource_title?(args)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  args.first.is_a?(Symbol) &&
         
     | 
| 
      
 76 
     | 
    
         
            +
                    (args[1].respond_to?(:model_name) || args[1].class.respond_to?(:model_name))
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def page_title_for_string(args)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  title = html_escape(args.first)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  options = args.last.is_a?(Hash) ? args.last : {}
         
     | 
| 
      
 82 
     | 
    
         
            +
                  if back_options = options[:back]
         
     | 
| 
      
 83 
     | 
    
         
            +
                    url =
         
     | 
| 
      
 84 
     | 
    
         
            +
                      case back_options
         
     | 
| 
      
 85 
     | 
    
         
            +
                      when Array then polymorphic_path(back_options)
         
     | 
| 
      
 86 
     | 
    
         
            +
                      when true then :back
         
     | 
| 
      
 87 
     | 
    
         
            +
                      else back_options
         
     | 
| 
      
 88 
     | 
    
         
            +
                      end
         
     | 
| 
      
 89 
     | 
    
         
            +
                    if url
         
     | 
| 
      
 90 
     | 
    
         
            +
                      back_link = link_to content_tag(:i, nil, class: 'left arrow icon'), url, class: 'title-back-link'
         
     | 
| 
      
 91 
     | 
    
         
            +
                      title = back_link.safe_concat(title)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  title
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                def page_title_for_resource(args)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  options = args.extract_options!
         
     | 
| 
      
 99 
     | 
    
         
            +
                  model = args[1].respond_to?(:model_name) ? args[1] : args[1].class
         
     | 
| 
      
 100 
     | 
    
         
            +
                  if args.first == :index
         
     | 
| 
      
 101 
     | 
    
         
            +
                    title = t('action.index.label', models: model.model_name.human_plural).html_safe
         
     | 
| 
      
 102 
     | 
    
         
            +
                  else
         
     | 
| 
      
 103 
     | 
    
         
            +
                    title = t("action.#{args.first}.label", model: model.model_name.human).html_safe
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
                  if back_options = options[:back]
         
     | 
| 
      
 106 
     | 
    
         
            +
                    url =
         
     | 
| 
      
 107 
     | 
    
         
            +
                      case back_options
         
     | 
| 
      
 108 
     | 
    
         
            +
                      when Array then polymorphic_path(back_options)
         
     | 
| 
      
 109 
     | 
    
         
            +
                      when true then :back
         
     | 
| 
      
 110 
     | 
    
         
            +
                      else back_options
         
     | 
| 
      
 111 
     | 
    
         
            +
                      end
         
     | 
| 
      
 112 
     | 
    
         
            +
                    if url
         
     | 
| 
      
 113 
     | 
    
         
            +
                      back_link = link_to content_tag(:i, nil, class: 'left arrow icon'), url, class: 'title-back-link'
         
     | 
| 
      
 114 
     | 
    
         
            +
                      title = back_link.safe_concat(title)
         
     | 
| 
      
 115 
     | 
    
         
            +
                    end
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
                  title
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                def search_result_info(records)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  from_item = (records.current_page - 1) * records.limit_value + 1
         
     | 
| 
      
 122 
     | 
    
         
            +
                  to_item = [from_item + records.size - 1, records.total_count].min
         
     | 
| 
      
 123 
     | 
    
         
            +
                  from_item = 0 if records.total_count.zero?
         
     | 
| 
      
 124 
     | 
    
         
            +
                  "#{from_item} - #{to_item} / #{records.total_count}"
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                def at(attribute_name, scope_model=nil)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  scope_model ||= @scope_model
         
     | 
| 
      
 129 
     | 
    
         
            +
                  scope_model.human_attribute_name(attribute_name)
         
     | 
| 
      
 130 
     | 
    
         
            +
                end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                # Search helpers from dunlop
         
     | 
| 
      
 133 
     | 
    
         
            +
                def search_row(options = {}, &blk)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  classes = Array.wrap(options[:class])
         
     | 
| 
      
 135 
     | 
    
         
            +
                  classes |= ['search']
         
     | 
| 
      
 136 
     | 
    
         
            +
                  classes << 'conditions-present' if @q.conditions.present?
         
     | 
| 
      
 137 
     | 
    
         
            +
                  content = capture(&blk)
         
     | 
| 
      
 138 
     | 
    
         
            +
                  content_tag(:tr, content,  class: classes )
         
     | 
| 
      
 139 
     | 
    
         
            +
                end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                # This helper returns the link for showing a record inside a table
         
     | 
| 
      
 142 
     | 
    
         
            +
                def table_show_link(record, path = nil, options = {})
         
     | 
| 
      
 143 
     | 
    
         
            +
                  if options.has_key?(:authorized)
         
     | 
| 
      
 144 
     | 
    
         
            +
                    return unless options[:authorized]
         
     | 
| 
      
 145 
     | 
    
         
            +
                  else
         
     | 
| 
      
 146 
     | 
    
         
            +
                    return unless can? :show, record
         
     | 
| 
      
 147 
     | 
    
         
            +
                  end
         
     | 
| 
      
 148 
     | 
    
         
            +
                  link_to(content_tag(:i,nil, class: 'folder open icon'), path || record, class: 'table-link show ui mini basic primary icon button')
         
     | 
| 
      
 149 
     | 
    
         
            +
                end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                # This helper returns the link for showing a record inside a table
         
     | 
| 
      
 152 
     | 
    
         
            +
                def table_download_link(record, path = nil, options = {})
         
     | 
| 
      
 153 
     | 
    
         
            +
                  if options.has_key?(:authorized)
         
     | 
| 
      
 154 
     | 
    
         
            +
                    return unless options[:authorized]
         
     | 
| 
      
 155 
     | 
    
         
            +
                  else
         
     | 
| 
      
 156 
     | 
    
         
            +
                    return unless can? :download, record
         
     | 
| 
      
 157 
     | 
    
         
            +
                  end
         
     | 
| 
      
 158 
     | 
    
         
            +
                  link_to(content_tag(:i,nil, class: 'download icon'), path || [:download, record], class: 'table-link download ui mini violet icon button')
         
     | 
| 
      
 159 
     | 
    
         
            +
                end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                # This helper returns the link for editing a record inside a table
         
     | 
| 
      
 162 
     | 
    
         
            +
                def table_edit_link(record, path = nil, options = {})
         
     | 
| 
      
 163 
     | 
    
         
            +
                  if options.has_key?(:authorized)
         
     | 
| 
      
 164 
     | 
    
         
            +
                    return unless options[:authorized]
         
     | 
| 
      
 165 
     | 
    
         
            +
                  else
         
     | 
| 
      
 166 
     | 
    
         
            +
                    return unless can? :update, record
         
     | 
| 
      
 167 
     | 
    
         
            +
                  end
         
     | 
| 
      
 168 
     | 
    
         
            +
                  link_to(content_tag(:i, nil, class: 'write icon'), path || [:edit, record], class: 'table-link edit ui mini basic yellow icon button')
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                def table_destroy_link(record, path = nil, options = {})
         
     | 
| 
      
 172 
     | 
    
         
            +
                  if options.has_key?(:authorized)
         
     | 
| 
      
 173 
     | 
    
         
            +
                    return unless options[:authorized]
         
     | 
| 
      
 174 
     | 
    
         
            +
                  else
         
     | 
| 
      
 175 
     | 
    
         
            +
                    return unless can? :destroy, record
         
     | 
| 
      
 176 
     | 
    
         
            +
                  end
         
     | 
| 
      
 177 
     | 
    
         
            +
                  confirm_text = "Are you sure you want to delete #{record.class.model_name.human}"
         
     | 
| 
      
 178 
     | 
    
         
            +
                  record_name = nil
         
     | 
| 
      
 179 
     | 
    
         
            +
                  record_name = record.presentation_name if record.respond_to?(:presentation_name)
         
     | 
| 
      
 180 
     | 
    
         
            +
                  record_name ||= record.name if record.respond_to?(:name)
         
     | 
| 
      
 181 
     | 
    
         
            +
                  record_name ||= record.title if record.respond_to?(:title)
         
     | 
| 
      
 182 
     | 
    
         
            +
                  confirm_text << " #{record_name}" if record_name.present?
         
     | 
| 
      
 183 
     | 
    
         
            +
                  confirm_text << "?"
         
     | 
| 
      
 184 
     | 
    
         
            +
                  link_to(content_tag(:i, nil, class: 'trash icon'), path || record, method: :delete, data: { confirm: confirm_text }, class: 'table-link destroy ui mini negative icon button')
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                # https://coderwall.com/p/7gqmog/display-flash-messages-with-semantic-ui-in-rails
         
     | 
| 
      
 188 
     | 
    
         
            +
                def flash_class(level)
         
     | 
| 
      
 189 
     | 
    
         
            +
                  case level.to_sym
         
     | 
| 
      
 190 
     | 
    
         
            +
                  when :success       then "ui positive message"
         
     | 
| 
      
 191 
     | 
    
         
            +
                  when :error, :alert then "ui negative message"
         
     | 
| 
      
 192 
     | 
    
         
            +
                  when :notice        then "ui info message"
         
     | 
| 
      
 193 
     | 
    
         
            +
                  else "ui #{level} message"
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
              end
         
     | 
| 
      
 197 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Contact < ApplicationRecord
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend Geocoder::Model::ActiveRecord
         
     | 
| 
      
 4 
     | 
    
         
            +
                has_many :contact_labels, dependent: :delete_all
         
     | 
| 
      
 5 
     | 
    
         
            +
                has_many :labels, through: :contact_labels
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                geocoded_by :address
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                after_validation :geocode_if_necessary
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def self.for_label_ids(label_ids, predication: 'and')
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #predication = %w[and or].include?(predication) ? predication : 'and' #whitlelisting
         
     | 
| 
      
 13 
     | 
    
         
            +
                  if predication == 'or'
         
     | 
| 
      
 14 
     | 
    
         
            +
                    joins(:labels).merge(Label.descendants_for_ids(label_ids)).distinct
         
     | 
| 
      
 15 
     | 
    
         
            +
                  else
         
     | 
| 
      
 16 
     | 
    
         
            +
                    where id: ContactLabel.contact_ids_for_label_ids(label_ids)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def label_ids=(array_or_string)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  super array_or_string.is_a?(Array) ? array_or_string : array_or_string.split(',')
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def address
         
     | 
| 
      
 25 
     | 
    
         
            +
                  [street, city, state, country].map(&:presence).compact.join(', ')
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def address_changed?
         
     | 
| 
      
 29 
     | 
    
         
            +
                  street_changed? or city_changed? or state_changed? or country_changed?
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def full_name
         
     | 
| 
      
 33 
     | 
    
         
            +
                  [first_name, middle_name, last_name].map(&:presence).compact.join(' ')
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                ransacker :full_name do |parent|
         
     | 
| 
      
 37 
     | 
    
         
            +
                  Arel::Nodes::InfixOperation.new('||', Arel::Nodes::InfixOperation.new('||', parent.table[:first_name], parent.table[:middle_name]), parent.table[:last_name])
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def map_title
         
     | 
| 
      
 41 
     | 
    
         
            +
                  full_name
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def map_attributes
         
     | 
| 
      
 45 
     | 
    
         
            +
                  return {} unless geocoded?
         
     | 
| 
      
 46 
     | 
    
         
            +
                  {
         
     | 
| 
      
 47 
     | 
    
         
            +
                    lat: latitude,
         
     | 
| 
      
 48 
     | 
    
         
            +
                    lng: longitude,
         
     | 
| 
      
 49 
     | 
    
         
            +
                    title: map_title
         
     | 
| 
      
 50 
     | 
    
         
            +
                  }
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def geocode_if_necessary
         
     | 
| 
      
 54 
     | 
    
         
            +
                  return if new_record? and latitude.present? and longitude.present?
         
     | 
| 
      
 55 
     | 
    
         
            +
                  if address_changed?
         
     | 
| 
      
 56 
     | 
    
         
            +
                    self.latitude = nil
         
     | 
| 
      
 57 
     | 
    
         
            +
                    self.longitude = nil
         
     | 
| 
      
 58 
     | 
    
         
            +
                    # Geocode addresses with at least 2 commas
         
     | 
| 
      
 59 
     | 
    
         
            +
                    geocode if address.count(',') > 1
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
              class ContactLabel < ApplicationRecord
         
     | 
| 
      
 3 
     | 
    
         
            +
                belongs_to :contact
         
     | 
| 
      
 4 
     | 
    
         
            +
                belongs_to :label
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                ### The strategy
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Join all possible collection label matches
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Then expec the amount of found matches to be the amount of requested labels (match all label groups).
         
     | 
| 
      
 9 
     | 
    
         
            +
                # Sadly this stragegy fails when a contact has to labels assigned (Indonesian AND Vietnamese food), and
         
     | 
| 
      
 10 
     | 
    
         
            +
                # the query is for all food categories (parent).
         
     | 
| 
      
 11 
     | 
    
         
            +
                # Works for simple and concice situations. Not for multi label definitions
         
     | 
| 
      
 12 
     | 
    
         
            +
                def self.contact_ids_for_label_ids2(label_ids)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  scope = joins(:label).merge(Label.descendants_for_ids(label_ids)).select(:contact_id).group(:contact_id)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  scope = scope.having(arel_table[:contact_id].count.eq label_ids.size).reorder('')
         
     | 
| 
      
 16 
     | 
    
         
            +
                  scope
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                ### The strategy:
         
     | 
| 
      
 20 
     | 
    
         
            +
                # Create a subquery for each label collection possible situation
         
     | 
| 
      
 21 
     | 
    
         
            +
                # Then join all these subqueries on the contact_id to remain only
         
     | 
| 
      
 22 
     | 
    
         
            +
                # with the records having the contact_id in each of these subqueries
         
     | 
| 
      
 23 
     | 
    
         
            +
                def self.contact_ids_for_label_ids(label_ids)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  records = Label.find(label_ids)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  scope = joins(%|INNER JOIN "geo_labels_labels" ON "geo_labels_labels"."id" = "geo_labels_contact_labels"."label_id"|).merge(records.shift.self_and_descendants).reorder('').select(:contact_id)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  records.each do |record|
         
     | 
| 
      
 30 
     | 
    
         
            +
                    record_scope = joins(:label).merge(record.self_and_descendants).reorder('').select(:contact_id)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    join_table_name = "labeltable#{record.id}"
         
     | 
| 
      
 32 
     | 
    
         
            +
                    join_table_contact_id = Arel::Nodes::SqlLiteral.new("#{join_table_name}.contact_id")
         
     | 
| 
      
 33 
     | 
    
         
            +
                    query_str = %|INNER JOIN (#{record_scope.to_sql}) #{join_table_name} ON #{arel_table[:contact_id].eq(join_table_contact_id).to_sql}|
         
     | 
| 
      
 34 
     | 
    
         
            +
                    scope = scope.joins(query_str)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    #descendants_scope = descendants_scope.or(record.self_and_descendants)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  scope
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GeoLabels
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Label < ApplicationRecord
         
     | 
| 
      
 3 
     | 
    
         
            +
                has_many :contact_labels, dependent: :delete_all
         
     | 
| 
      
 4 
     | 
    
         
            +
                acts_as_nested_set
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def self.descendants_for_ids(ids)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  records = find(ids)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  descendants_scope = records.shift.self_and_descendants
         
     | 
| 
      
 9 
     | 
    
         
            +
                  records.each do |record|
         
     | 
| 
      
 10 
     | 
    
         
            +
                    descendants_scope = descendants_scope.or(record.self_and_descendants)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
                  descendants_scope
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            = link_to t('action.destroy.label', model: (record_class).model_name.human), record, class: 'ui red button', method: :delete, data: { confirm: t('general.are_you_sure') }
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            = link_to t('action.edit.label', model: (record_class).model_name.human), polymorphic_path(record, action: :edit), class: 'ui yellow button'
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            = link_to t('action.new.label', model: (record_class).model_name.human), polymorphic_path(record_class, action: :new), class: 'ui primary basic button'
         
     |