diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bf8c11..43823e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,8 +11,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ["3.2"] - gemfile: ["rails_7_0"] + ruby-version: ["3.2", "4.0"] + gemfile: ["rails_7_0", "rails_8_0"] + exclude: + - ruby-version: "4.0" + gemfile: "rails_7_0" + - ruby-version: "3.2" + gemfile: "rails_8_0" fail-fast: false env: @@ -38,7 +43,7 @@ jobs: --health-retries 5 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 # libgeos and gdal are required and must be installed before `bundle install` # NB: if you add libs here that are required for Gem native extensions be sure to clear the diff --git a/app/models/abstract_feature.rb b/app/models/abstract_feature.rb index fa0f5dc..8f1cf5b 100644 --- a/app/models/abstract_feature.rb +++ b/app/models/abstract_feature.rb @@ -7,7 +7,7 @@ class AbstractFeature < ActiveRecord::Base class_attribute :lowres_simplification self.lowres_simplification = 2 # Threshold in meters - belongs_to :spatial_model, :polymorphic => :true, :autosave => false + belongs_to :spatial_model, polymorphic: :true, autosave: false attr_writer :make_valid @@ -16,7 +16,7 @@ class AbstractFeature < ActiveRecord::Base validates_presence_of :geog validate :validate_geometry, if: :will_save_change_to_geog? before_save :sanitize, if: :will_save_change_to_geog? - after_save :cache_derivatives, :if => [:automatically_cache_derivatives?, :saved_change_to_geog?] + after_save :cache_derivatives, if: [:automatically_cache_derivatives?, :saved_change_to_geog?] def self.cache_key collection_cache_key @@ -41,15 +41,15 @@ def self.metadata_keys end def self.polygons - where(:feature_type => 'polygon') + where(feature_type: 'polygon') end def self.lines - where(:feature_type => 'line') + where(feature_type: 'line') end def self.points - where(:feature_type => 'point') + where(feature_type: 'point') end def self.within_distance_of_point(lat, lng, distance_in_meters, geom = 'geom_lowres') @@ -59,7 +59,7 @@ def self.within_distance_of_point(lat, lng, distance_in_meters, geom = 'geom_low else "ST_Transform(ST_SetSRID(ST_Point(:lng, :lat), 4326), #{detect_srid(geom)})" end - binds = { :lng => lng.to_d, :lat => lat.to_d } + binds = { lng: lng.to_d, lat: lat.to_d } within_distance_of_sql(point_sql, distance_in_meters, geom, **binds) end @@ -71,7 +71,7 @@ def self.within_distance_of_line(points, distance_in_meters, geom = 'geom_lowres else "ST_Transform(ST_SetSRID(:points::geometry, 4326), #{detect_srid(geom)})" end - binds = { :points => "LINESTRING(#{points.map {|coords| coords.join(' ') }.join(', ')})" } + binds = { points: "LINESTRING(#{points.map {|coords| coords.join(' ') }.join(', ')})" } within_distance_of_sql(point_sql, distance_in_meters, geom, **binds) end @@ -83,14 +83,14 @@ def self.within_distance_of_polygon(points, distance_in_meters, geom = 'geom_low else "ST_Transform(ST_Polygon(:points::geometry, 4326), #{detect_srid(geom)})" end - binds = { :points => "LINESTRING(#{points.map {|coords| coords.join(' ') }.join(', ')})" } + binds = { points: "LINESTRING(#{points.map {|coords| coords.join(' ') }.join(', ')})" } within_distance_of_sql(point_sql, distance_in_meters, geom, **binds) end def self.within_distance_of_sql(geometry_sql, distance_in_meters, features_column = 'geom_lowres', **binds) if distance_in_meters.to_f > 0 - where("ST_DWithin(features.#{features_column}, #{geometry_sql}, :distance)", **binds, :distance => distance_in_meters) + where("ST_DWithin(features.#{features_column}, #{geometry_sql}, :distance)", **binds, distance: distance_in_meters) else where("ST_Intersects(features.#{features_column}, #{geometry_sql})", **binds) end @@ -128,7 +128,7 @@ def self.valid end def envelope(buffer_in_meters = 0) - envelope_json = JSON.parse(self.class.select("ST_AsGeoJSON(ST_Envelope(ST_Buffer(features.geog, #{buffer_in_meters})::geometry)) AS result").where(:id => id).first.result) + envelope_json = JSON.parse(self.class.select("ST_AsGeoJSON(ST_Envelope(ST_Buffer(features.geog, #{buffer_in_meters})::geometry)) AS result").where(id: id).first.result) envelope_json = envelope_json["coordinates"].first raise "Can't calculate envelope for Feature #{self.id}" if envelope_json.blank? @@ -257,12 +257,12 @@ def bounds end def cache_derivatives(*args) - self.class.default_scoped.where(:id => self.id).cache_derivatives(*args) + self.class.default_scoped.where(id: self.id).cache_derivatives(*args) end def kml(options = {}) column = options[:lowres] ? 'geom_lowres' : 'geog' - return SpatialFeatures::Utils.select_db_value(self.class.where(:id => id).select("ST_AsKML(#{column}, 6)")) + return SpatialFeatures::Utils.select_db_value(self.class.where(id: id).select("ST_AsKML(#{column}, 6)")) end def geojson(*args) @@ -290,7 +290,7 @@ def self.detect_srid(column_name) end def self.join_other_features(other) - joins('INNER JOIN features AS other_features ON true').where(:other_features => {:id => other}) + joins('INNER JOIN features AS other_features ON true').where(other_features: {id: other}) end def validate_geometry diff --git a/app/models/aggregate_feature.rb b/app/models/aggregate_feature.rb index 4ebb7c0..ec90e93 100644 --- a/app/models/aggregate_feature.rb +++ b/app/models/aggregate_feature.rb @@ -1,10 +1,10 @@ require_dependency SpatialFeatures::Engine.root.join('app/models/abstract_feature') class AggregateFeature < AbstractFeature - has_many :features, lambda { |aggregate| where(:spatial_model_type => aggregate.spatial_model_type) }, :foreign_key => :spatial_model_id, :primary_key => :spatial_model_id + has_many :features, lambda { |aggregate| where(spatial_model_type: aggregate.spatial_model_type) }, foreign_key: :spatial_model_id, primary_key: :spatial_model_id # Aggregate the features for the spatial model into a single feature - before_validation :set_geog, :on => :create, :unless => :geog? + before_validation :set_geog, on: :create, unless: :geog? private diff --git a/app/models/feature.rb b/app/models/feature.rb index c9d3f95..6454dc7 100644 --- a/app/models/feature.rb +++ b/app/models/feature.rb @@ -7,9 +7,9 @@ class Feature < AbstractFeature class_attribute :lowres_precision self.lowres_precision = 5 - has_one :aggregate_feature, lambda { |feature| where(:spatial_model_type => feature.spatial_model_type) }, :foreign_key => :spatial_model_id, :primary_key => :spatial_model_id + has_one :aggregate_feature, lambda { |feature| where(spatial_model_type: feature.spatial_model_type) }, foreign_key: :spatial_model_id, primary_key: :spatial_model_id - scope :source_identifier, lambda {|source_identifier| where(:source_identifier => source_identifier) if source_identifier.present? } + scope :source_identifier, lambda {|source_identifier| where(source_identifier: source_identifier) if source_identifier.present? } before_save :truncate_name @@ -21,7 +21,7 @@ def self.defer_aggregate_refresh(&block) start_at = Feature.maximum(:id).to_i + 1 output = without_aggregate_refresh(&block) - where(:id => start_at..Float::INFINITY).refresh_aggregates + where(id: start_at..Float::INFINITY).refresh_aggregates return output end @@ -36,13 +36,13 @@ def self.without_aggregate_refresh def self.refresh_aggregates # Find one feature from each spatial model and trigger the aggregate feature refresh - ids = where.not(:spatial_model_type => nil) - .where.not(:spatial_model_id => nil) + ids = where.not(spatial_model_type: nil) + .where.not(spatial_model_id: nil) .group('spatial_model_type, spatial_model_id') .pluck('MAX(id)') # Unscope so that newly built AggregateFeatures get their type column set correctly - AbstractFeature.unscoped { where(:id => ids).find_each(&:refresh_aggregate) } + AbstractFeature.unscoped { where(id: ids).find_each(&:refresh_aggregate) } end def refresh_aggregate diff --git a/app/models/spatial_cache.rb b/app/models/spatial_cache.rb index 1d40d4c..e88d786 100644 --- a/app/models/spatial_cache.rb +++ b/app/models/spatial_cache.rb @@ -1,9 +1,9 @@ class SpatialCache < ActiveRecord::Base - belongs_to :spatial_model, :polymorphic => true, :inverse_of => :spatial_caches + belongs_to :spatial_model, polymorphic: true, inverse_of: :spatial_caches def self.between(spatial_model, klass) where(SpatialFeatures::Utils.polymorphic_condition(spatial_model, 'spatial_model')) - .where(:intersection_model_type => SpatialFeatures::Utils.class_name_with_ancestors(klass)) + .where(intersection_model_type: SpatialFeatures::Utils.class_name_with_ancestors(klass)) end def stale? diff --git a/app/models/spatial_proximity.rb b/app/models/spatial_proximity.rb index 0e98693..2c76b98 100644 --- a/app/models/spatial_proximity.rb +++ b/app/models/spatial_proximity.rb @@ -1,6 +1,6 @@ class SpatialProximity < ActiveRecord::Base - belongs_to :model_a, :polymorphic => true - belongs_to :model_b, :polymorphic => true + belongs_to :model_a, polymorphic: true + belongs_to :model_b, polymorphic: true def self.between(scope1, scope2) where condition_sql(scope1, scope2, <<~SQL.squish) diff --git a/gemfiles/rails_6_1.gemfile b/gemfiles/rails_6_1.gemfile deleted file mode 100644 index 6c13704..0000000 --- a/gemfiles/rails_6_1.gemfile +++ /dev/null @@ -1,5 +0,0 @@ -source "https://rubygems.org" - -gem "activerecord", "~> 6.1", "< 6.2" - -gemspec path: "../" diff --git a/gemfiles/rails_6_0.gemfile b/gemfiles/rails_8_0.gemfile similarity index 57% rename from gemfiles/rails_6_0.gemfile rename to gemfiles/rails_8_0.gemfile index 32a1c96..019cc0b 100644 --- a/gemfiles/rails_6_0.gemfile +++ b/gemfiles/rails_8_0.gemfile @@ -1,5 +1,5 @@ source "https://rubygems.org" -gem "activerecord", "~> 6.0", "< 6.1" +gem "activerecord", "~> 8" gemspec path: "../" diff --git a/lib/spatial_features/caching.rb b/lib/spatial_features/caching.rb index 6c3b1a1..826840a 100644 --- a/lib/spatial_features/caching.rb +++ b/lib/spatial_features/caching.rb @@ -69,7 +69,7 @@ def self.clear_cache(klass = nil, clazz = nil) end def self.clear_record_cache(record, klass) - record.spatial_caches.where(:intersection_model_type => SpatialFeatures::Utils.class_name_with_ancestors(klass)).delete_all + record.spatial_caches.where(intersection_model_type: SpatialFeatures::Utils.class_name_with_ancestors(klass)).delete_all SpatialProximity.between(record, klass).delete_all end @@ -77,8 +77,8 @@ def self.create_spatial_proximities(record, klass) klass = klass.to_s.constantize klass_record = klass.new - scope = klass.within_buffer(record, default_cache_buffer_in_meters, :columns => :id, :intersection_area => true, :distance => true, :cache => false) - scope = scope.where.not(:id => record.id) if klass.table_name == record.class.table_name # Don't calculate self proximity + scope = klass.within_buffer(record, default_cache_buffer_in_meters, columns: :id, intersection_area: true, distance: true, cache: false) + scope = scope.where.not(id: record.id) if klass.table_name == record.class.table_name # Don't calculate self proximity results = klass.connection.select_rows(scope.to_sql) results.each do |id, distance, area| diff --git a/lib/spatial_features/controller_helpers/spatial_extensions.rb b/lib/spatial_features/controller_helpers/spatial_extensions.rb index 6a8b259..974b28e 100644 --- a/lib/spatial_features/controller_helpers/spatial_extensions.rb +++ b/lib/spatial_features/controller_helpers/spatial_extensions.rb @@ -15,21 +15,21 @@ def abstract_clear_feature_update_errors(models) end def abstract_proximity_action(scope, target, distance, &block) - @nearby_records = scope_for_search(scope).within_buffer(target, distance, :distance => true, :intersection_area => true).order('distance_in_meters ASC, intersection_area_in_square_meters DESC, id ASC') + @nearby_records = scope_for_search(scope).within_buffer(target, distance, distance: true, intersection_area: true).order('distance_in_meters ASC, intersection_area_in_square_meters DESC, id ASC') @target = target if block_given? block.call(@nearby_records) else respond_to do |format| - format.html { render :template => 'shared/spatial/feature_proximity', :layout => false } - format.kml { render :template => 'shared/spatial/feature_proximity' } + format.html { render template: 'shared/spatial/feature_proximity', layout: false } + format.kml { render template: 'shared/spatial/feature_proximity' } end end end def abstract_venn_polygons_action(scope, target, &block) - @venn_polygons = SpatialFeatures.venn_polygons(scope_for_search(scope).intersecting(target), target.class.where(:id => target), :target => target) + @venn_polygons = SpatialFeatures.venn_polygons(scope_for_search(scope).intersecting(target), target.class.where(id: target), target: target) @klass = klass_for_search(scope) @target = target @@ -37,7 +37,7 @@ def abstract_venn_polygons_action(scope, target, &block) block.call(@venn_polygons) else respond_to do |format| - format.kml { render :template => 'shared/spatial/feature_venn_polygons' } + format.kml { render template: 'shared/spatial/feature_venn_polygons' } end end end @@ -50,7 +50,7 @@ def scope_for_search(scope) if params.key?(:ids) ids = params[:ids] ids = ids.split(/\D/) if ids.is_a?(String) - scope.where(:id => ids) + scope.where(id: ids) else scope end diff --git a/lib/spatial_features/download.rb b/lib/spatial_features/download.rb index 788d3ea..5d3295b 100644 --- a/lib/spatial_features/download.rb +++ b/lib/spatial_features/download.rb @@ -36,7 +36,7 @@ def self.entries(file) end def self.find_in_zip(file, find:, **unzip_options) - Unzip.paths(file, :find => find, **unzip_options) + Unzip.paths(file, find: find, **unzip_options) end end end diff --git a/lib/spatial_features/has_spatial_features.rb b/lib/spatial_features/has_spatial_features.rb index 98e5225..5bea814 100644 --- a/lib/spatial_features/has_spatial_features.rb +++ b/lib/spatial_features/has_spatial_features.rb @@ -4,29 +4,29 @@ module ActMethod def has_spatial_features(options = {}) unless acts_like?(:spatial_features) class_attribute :spatial_features_options - self.spatial_features_options = {:make_valid => true} + self.spatial_features_options = {make_valid: true} extend ClassMethods include InstanceMethods include FeatureImport - has_many :features, lambda { extending FeaturesAssociationExtensions }, :as => :spatial_model, :dependent => :delete_all - has_one :aggregate_feature, lambda { extending FeaturesAssociationExtensions }, :as => :spatial_model, :dependent => :delete + has_many :features, lambda { extending FeaturesAssociationExtensions }, as: :spatial_model, dependent: :delete_all + has_one :aggregate_feature, lambda { extending FeaturesAssociationExtensions }, as: :spatial_model, dependent: :delete scope :with_features, lambda { joins(:features).distinct } scope :without_features, lambda { joins("LEFT OUTER JOIN features ON features.spatial_model_type = '#{Utils.base_class(name)}' AND features.spatial_model_id = #{table_name}.id").where("features.id IS NULL") } scope :include_bounds, lambda { SQLHelpers.append_select(joins(:aggregate_feature), :north, :east, :south, :west) } scope :include_area, lambda { SQLHelpers.append_select(joins(:aggregate_feature), :area) } - scope :with_spatial_cache, lambda {|klass| joins(:spatial_caches).where(:spatial_caches => { :intersection_model_type => Utils.class_name_with_ancestors(klass) }).distinct } + scope :with_spatial_cache, lambda {|klass| joins(:spatial_caches).where(spatial_caches: { intersection_model_type: Utils.class_name_with_ancestors(klass) }).distinct } scope :without_spatial_cache, lambda {|klass| joins("LEFT OUTER JOIN #{SpatialCache.table_name} ON #{SpatialCache.table_name}.spatial_model_id = #{table_name}.id AND #{SpatialCache.table_name}.spatial_model_type = '#{Utils.base_class(name)}' and intersection_model_type IN ('#{Utils.class_name_with_ancestors(klass).join("','") }')").where("#{SpatialCache.table_name}.spatial_model_id IS NULL") } scope :with_stale_spatial_cache, lambda { has_spatial_features_hash? ? joins(:spatial_caches).where("#{table_name}.features_hash != spatial_caches.features_hash").distinct : none } - has_many :spatial_caches, :as => :spatial_model, :dependent => :delete_all, :class_name => 'SpatialCache' - has_many :model_a_spatial_proximities, :as => :model_a, :class_name => 'SpatialProximity', :dependent => :delete_all - has_many :model_b_spatial_proximities, :as => :model_b, :class_name => 'SpatialProximity', :dependent => :delete_all + has_many :spatial_caches, as: :spatial_model, dependent: :delete_all, class_name: 'SpatialCache' + has_many :model_a_spatial_proximities, as: :model_a, class_name: 'SpatialProximity', dependent: :delete_all + has_many :model_b_spatial_proximities, as: :model_b, class_name: 'SpatialProximity', dependent: :delete_all - delegate :has_spatial_features_hash?, :has_features_area?, :to => self + delegate :has_spatial_features_hash?, :has_features_area?, to: self end self.spatial_features_options = self.spatial_features_options.deep_merge(options) @@ -87,18 +87,18 @@ def bounds def features type = base_class.to_s # Rails stores polymorphic foreign keys as the base class if all == unscoped - Feature.where(:spatial_model_type => type) + Feature.where(spatial_model_type: type) else - Feature.where(:spatial_model_type => type, :spatial_model_id => all.unscope(:select)) + Feature.where(spatial_model_type: type, spatial_model_id: all.unscope(:select)) end end def aggregate_features type = base_class.to_s # Rails stores polymorphic foreign keys as the base class if all == unscoped - AggregateFeature.where(:spatial_model_type => type) + AggregateFeature.where(spatial_model_type: type) else - AggregateFeature.where(:spatial_model_type => type, :spatial_model_id => all.unscope(:select)) + AggregateFeature.where(spatial_model_type: type, spatial_model_id: all.unscope(:select)) end end @@ -119,7 +119,7 @@ def area_in_square_meters private def cached_within_buffer_scope(other, buffer_in_meters, options) - options = options.reverse_merge(:columns => "#{table_name}.*") + options = options.reverse_merge(columns: "#{table_name}.*") scope = cached_spatial_join(other) scope = scope.select(options[:columns]) scope = scope.where("spatial_proximities.distance_in_meters <= ?", buffer_in_meters) if buffer_in_meters @@ -142,7 +142,7 @@ def cached_spatial_join(other) end def uncached_within_buffer_scope(other, buffer_in_meters, options) - options = options.reverse_merge(:columns => "#{table_name}.*") + options = options.reverse_merge(columns: "#{table_name}.*") scope = spatial_join(other, buffer_in_meters) scope = scope.select(options[:columns]) @@ -168,8 +168,8 @@ def spatial_join(other, buffer = 0, table_alias = 'features', other_alias = 'oth def features_scope(other) scope = AggregateFeature - scope = scope.where(:spatial_model_type => Utils.base_class_of(other).to_s) - scope = scope.where(:spatial_model_id => other) unless Utils.class_of(other) == other + scope = scope.where(spatial_model_type: Utils.base_class_of(other).to_s) + scope = scope.where(spatial_model_id: other) unless Utils.class_of(other) == other return scope end diff --git a/lib/spatial_features/has_spatial_features/feature_import.rb b/lib/spatial_features/has_spatial_features/feature_import.rb index 99a1c92..680e9c7 100644 --- a/lib/spatial_features/has_spatial_features/feature_import.rb +++ b/lib/spatial_features/has_spatial_features/feature_import.rb @@ -9,7 +9,7 @@ module FeatureImport included do extend ActiveModel::Callbacks define_model_callbacks :update_features - spatial_features_options.reverse_merge!(:import => {}, :spatial_cache => [], :image_handlers => []) + spatial_features_options.reverse_merge!(import: {}, spatial_cache: [], image_handlers: []) end module ClassMethods @@ -73,7 +73,7 @@ def update_features_cache_key(cache_key) def update_features_area return unless has_attribute?(:features_area) - self.features_area = features.area(:cache => false) + self.features_area = features.area(cache: false) update_column :features_area, features_area unless new_record? end @@ -101,7 +101,7 @@ def spatial_feature_imports(import_options, make_valid, tmpdir) Array.wrap(send(data_method)).flat_map do |data| next unless data.present? - spatial_importer_from_name(importer_name).create_all(data, **options, :make_valid => make_valid, :tmpdir => tmpdir) + spatial_importer_from_name(importer_name).create_all(data, **options, make_valid: make_valid, tmpdir: tmpdir) end end.compact end diff --git a/lib/spatial_features/has_spatial_features/queued_spatial_processing.rb b/lib/spatial_features/has_spatial_features/queued_spatial_processing.rb index a880154..bd7275e 100644 --- a/lib/spatial_features/has_spatial_features/queued_spatial_processing.rb +++ b/lib/spatial_features/has_spatial_features/queued_spatial_processing.rb @@ -90,7 +90,7 @@ def spatial_processing_jobs(method_name = nil) def queue_spatial_task(method_name, *args, priority: 1, **kwargs) # NOTE: We pass kwargs as an arg because Delayed::Job does not support separation of positional and keyword arguments in Ruby 3.0. Instead we perform manual extraction in `perform`. - Delayed::Job.enqueue SpatialProcessingJob.new(self, method_name, *args, kwargs), :queue => spatial_processing_queue_name + method_name, priority: + Delayed::Job.enqueue SpatialProcessingJob.new(self, method_name, *args, kwargs), queue: spatial_processing_queue_name + method_name, priority: end def spatial_processing_queue_name @@ -111,7 +111,7 @@ def enqueue(job) end def before(job) - ids = running_jobs.where.not(:id => job.id).pluck(:id) + ids = running_jobs.where.not(id: job.id).pluck(:id) raise "Already processing delayed jobs in this spatial queue: Delayed::Job #{ids.to_sentence}." if ids.present? end @@ -141,8 +141,8 @@ def update_cached_status(state) def running_jobs @record.spatial_processing_jobs - .where(:locked_at => Delayed::Worker.max_run_time.ago..Time.current) - .where(:failed_at => nil) + .where(locked_at: Delayed::Worker.max_run_time.ago..Time.current) + .where(failed_at: nil) end end end diff --git a/lib/spatial_features/importers/esri_geo_json.rb b/lib/spatial_features/importers/esri_geo_json.rb index 62a7984..a734f11 100644 --- a/lib/spatial_features/importers/esri_geo_json.rb +++ b/lib/spatial_features/importers/esri_geo_json.rb @@ -1,4 +1,3 @@ -require 'ostruct' require 'digest/md5' require 'spatial_features/importers/geo_json' diff --git a/lib/spatial_features/importers/geo_json.rb b/lib/spatial_features/importers/geo_json.rb index 444e074..d70bfeb 100644 --- a/lib/spatial_features/importers/geo_json.rb +++ b/lib/spatial_features/importers/geo_json.rb @@ -17,10 +17,10 @@ def each_record(&block) metadata = record['properties'] || {} name = metadata.delete('name') yield OpenStruct.new( - :feature_type => record['geometry']['type'], - :geog => SpatialFeatures::Utils.geom_from_json(record['geometry']), - :name => name, - :metadata => metadata + feature_type: record['geometry']['type'], + geog: SpatialFeatures::Utils.geom_from_json(record['geometry']), + name: name, + metadata: metadata ) end end diff --git a/lib/spatial_features/importers/kml.rb b/lib/spatial_features/importers/kml.rb index 3aae2fc..03a4b9e 100644 --- a/lib/spatial_features/importers/kml.rb +++ b/lib/spatial_features/importers/kml.rb @@ -36,7 +36,7 @@ def each_record(&block) importable_image_paths = images_from_metadata(metadata) - yield OpenStruct.new(:geog => geog, :name => name, :metadata => metadata, :importable_image_paths => importable_image_paths) + yield OpenStruct.new(geog: geog, name: name, metadata: metadata, importable_image_paths: importable_image_paths) end end end @@ -92,7 +92,7 @@ def extract_metadata(placemark) metadata = {} metadata.merge! extract_table(placemark) metadata.merge! extract_extended_data(placemark) - metadata.merge! :description => placemark.css('description').text if metadata.empty? + metadata.merge! description: placemark.css('description').text if metadata.empty? metadata.delete_if {|key, value| value.blank? } return metadata diff --git a/lib/spatial_features/importers/kml_file_arcgis.rb b/lib/spatial_features/importers/kml_file_arcgis.rb index 4105af8..ad1d34c 100644 --- a/lib/spatial_features/importers/kml_file_arcgis.rb +++ b/lib/spatial_features/importers/kml_file_arcgis.rb @@ -1,4 +1,3 @@ -require 'ostruct' module SpatialFeatures module Importers diff --git a/lib/spatial_features/importers/shapefile.rb b/lib/spatial_features/importers/shapefile.rb index 65c6e30..d3626bb 100644 --- a/lib/spatial_features/importers/shapefile.rb +++ b/lib/spatial_features/importers/shapefile.rb @@ -30,7 +30,7 @@ def self.create_all(data, **options) def each_record open_shapefile(archive) do |records, proj4| records.each do |record| - yield OpenStruct.new data_from_record(record, proj4) if record.geometry.present? + yield OpenStruct.new(**data_from_record(record, proj4)) if record.geometry.present? end end rescue Errno::ENOENT => e @@ -45,7 +45,7 @@ def each_record def data_from_record(record, proj4 = nil) geometry = record.geometry wkt = geometry.as_text - data = { :metadata => record.attributes } + data = { metadata: record.attributes } if proj4 == PROJ4_4326 data[:geog] = wkt @@ -66,7 +66,7 @@ def open_shapefile(file, &block) validate_shapefile!(file.path) proj4 = proj4_projection(file.path) - RGeo::Shapefile::Reader.open(file.path, :allow_unsafe => true) do |records| # Fall back to unprojected geometry if projection fails + RGeo::Shapefile::Reader.open(file.path, allow_unsafe: true) do |records| # Fall back to unprojected geometry if projection fails block.call records, proj4 end ensure diff --git a/lib/spatial_features/venn_polygons.rb b/lib/spatial_features/venn_polygons.rb index feaecb5..6965db1 100644 --- a/lib/spatial_features/venn_polygons.rb +++ b/lib/spatial_features/venn_polygons.rb @@ -1,3 +1,5 @@ +require 'ostruct' + module SpatialFeatures # Splits overlapping features into separate polygons at their areas of overlap, and returns an array of objects # with kml for the overlapping area and a list of the record ids whose kml overlapped within that area @@ -57,7 +59,7 @@ def self.venn_polygons(*scopes) polygons.group_by{|row| row['kml']}.collect do |kml, rows| # Uniq on row id in case a single record had self intersecting multi geometry, which would cause it to appear duplicated on a single venn polygon records = rows.uniq {|row| row.values_at('id', 'type') }.collect{|row| eager_load_hash.fetch(row['type']).detect{|record| record.id == row['id'].to_i } } - OpenStruct.new(:kml => kml, :records => records) + OpenStruct.new(kml: kml, records: records) end end end diff --git a/spatial_features.gemspec b/spatial_features.gemspec index 7def31d..e234fa2 100644 --- a/spatial_features.gemspec +++ b/spatial_features.gemspec @@ -17,14 +17,15 @@ Gem::Specification.new do |s| s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] s.test_files = Dir["test/**/*"] - s.add_runtime_dependency "rails", '>= 6', '< 8' + s.add_runtime_dependency "rails", '>= 6', '< 9' s.add_runtime_dependency "delayed_job_active_record", '~> 4.1' s.add_runtime_dependency "rgeo-shapefile", '~> 3.1' - s.add_runtime_dependency "rgeo-geojson", '~> 2.1.1' + s.add_runtime_dependency "rgeo-geojson", '>= 2.1.1' s.add_runtime_dependency "rubyzip", "~> 3.0" s.add_runtime_dependency "nokogiri" + s.add_runtime_dependency "ostruct" - s.add_development_dependency "rails", '>= 7', '< 8' + s.add_development_dependency "rails", '>= 7', '< 9' s.add_development_dependency "pg", '~> 1' s.add_development_dependency "rspec", '~> 3.5' s.add_development_dependency 'pry-byebug'