g5_updatable 0.10.3 → 0.20.3.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -2
  3. data/app/concerns/g5_updatable/belongs_to_client.rb +3 -3
  4. data/app/controllers/g5_updatable/feed_controller.rb +1 -1
  5. data/app/controllers/g5_updatable/locations_controller.rb +2 -2
  6. data/app/controllers/g5_updatable/syncs_controller.rb +3 -3
  7. data/app/models/g5_updatable/client.rb +4 -4
  8. data/app/models/g5_updatable/hub_amenities_location.rb +6 -0
  9. data/app/models/g5_updatable/hub_amenity.rb +21 -0
  10. data/app/models/g5_updatable/location.rb +75 -2
  11. data/app/serializers/g5_updatable/location_serializer.rb +8 -0
  12. data/db/migrate/20151103043916_add_latitude_and_longitude_to_location.rb +6 -0
  13. data/db/migrate/20151103050229_copy_lat_long_props_to_lat_long_columns.rb +19 -0
  14. data/db/migrate/20151106070749_add_latitude_longitude_indexes_to_location.rb +6 -0
  15. data/db/migrate/20161122070749_add_amenities.rb +25 -0
  16. data/db/migrate/20161209070749_add_client_urn_to_locations.rb +6 -0
  17. data/lib/g5_updatable.rb +5 -3
  18. data/lib/g5_updatable/all_client_urns_fetcher.rb +17 -0
  19. data/lib/g5_updatable/client_feed_processor.rb +26 -14
  20. data/lib/g5_updatable/client_updater.rb +37 -12
  21. data/lib/g5_updatable/factories.rb +2 -2
  22. data/lib/g5_updatable/fetcher.rb +22 -0
  23. data/lib/g5_updatable/indifferentizer.rb +11 -0
  24. data/lib/g5_updatable/locations_updater.rb +68 -20
  25. data/lib/g5_updatable/rspec/factories.rb +53 -2
  26. data/lib/g5_updatable/version.rb +1 -1
  27. data/lib/tasks/g5_updatable_tasks.rake +11 -4
  28. data/spec/concerns/g5_updatable/belongs_to_client_spec.rb +1 -3
  29. data/spec/controllers/feed_controller_spec.rb +21 -0
  30. data/spec/controllers/syncs_controller_spec.rb +3 -3
  31. data/spec/dummy/config/database.yml +2 -2
  32. data/spec/dummy/db/schema.rb +28 -1
  33. data/spec/dummy/log/development.log +172 -146
  34. data/spec/dummy/log/test.log +101011 -13426
  35. data/spec/dummy/log/tests.log +0 -0
  36. data/spec/fixtures/client-g5-c-1soj8m6e-g5-multifamily-missing-locations.json +121 -0
  37. data/spec/fixtures/client-g5-c-1soj8m6e-g5-multifamily-no-locations.json +41 -0
  38. data/spec/fixtures/client-g5-c-1soj8m6e-g5-multifamily.json +471 -0
  39. data/spec/fixtures/hub-client.json +187 -0
  40. data/spec/fixtures/hub-clients.json +19972 -0
  41. data/spec/fixtures/hub-location.json +166 -0
  42. data/spec/fixtures/location-g5-cl-1soj9pe2-541-apartments.json +98 -0
  43. data/spec/fixtures/urns.json +10 -0
  44. data/spec/lib/g5_updatable/all_client_urns_fetcher_spec.rb +56 -0
  45. data/spec/lib/g5_updatable/client_feed_processor_spec.rb +86 -33
  46. data/spec/lib/g5_updatable/client_updater_spec.rb +55 -23
  47. data/spec/lib/g5_updatable/locations_updater_spec.rb +140 -54
  48. data/spec/models/g5_updatable/client_spec.rb +2 -0
  49. data/spec/models/g5_updatable/hub_amenities_location_spec.rb +6 -0
  50. data/spec/models/g5_updatable/hub_amenity_spec.rb +29 -0
  51. data/spec/models/g5_updatable/location_spec.rb +240 -10
  52. data/spec/serializers/g5_updatable/location_serializer_spec.rb +2 -1
  53. data/spec/spec_helper.rb +2 -1
  54. data/spec/support/fixture_helper.rb +5 -0
  55. data/spec/support/shared_examples/belongs_to_client.rb +4 -5
  56. metadata +84 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3afda87d522137bbbd0e889b22b17ba948be94ff
4
- data.tar.gz: f7baac28c58eb4cdcf78ab270a23aeedeb24c46a
3
+ metadata.gz: fff24c03cbb209168e956147a0266737b9bcfba9
4
+ data.tar.gz: b459baf96623cb716ed624e606307f1712359d40
5
5
  SHA512:
6
- metadata.gz: 58b4ce1062ed6e2a9ca0282e9f56307bedcee380600d58959dac5de02bbda2b5f62bd8300c4f63088931d1e2974ca1abd678302f85bca9755ed414be4af4ced6
7
- data.tar.gz: 129bb7ea507eb372da5be65b9bae5397cc4a92629cc074e3b4ff76251ab2f13420b324d593db5f53ae95055ec4e44edc234abf7b38e36292abbcd7ecf09c36da
6
+ metadata.gz: fccf2277a17f67d1c7945ef14c90084b60eb11d42e5937352b4cefe52b45fa79d704f857329593df4bddf78a3571d0987b4b731bf0424d1e30c1e7b1ce35e825
7
+ data.tar.gz: c550dc2f70ec537c76570849bc2f54b637367b5ad5c9c55bf59dae1bae1d1d2acf99adb11b97ef4d97b70993a50ebd74829106e3e7028056be686db8af4aac0c
data/README.md CHANGED
@@ -38,9 +38,12 @@ G5 Updatable makes use of PostgrSQL's `json` field type, and so only supports im
38
38
  ```
39
39
  3. Optional: load all of G5-Hub's data into your database
40
40
 
41
+ **Important:** The entries.js and entries.json endpoint on the hub are deprecated. So, for all versions of g5_updatable before 0.17.0, this task will only load the newest 10 clients. If you would like to load all clients, please specify "~> 0.17.0" or higher in your Gemfile.
42
+
41
43
  ```console
42
44
  rake g5_updatable:load_all
43
- ```
45
+ ```
46
+
44
47
  Note, all of the G5_AUTH env variables need to be set for this to work.
45
48
 
46
49
  ## Usage
@@ -58,14 +61,42 @@ G5 Updatable also exposes two GET endpoints which return JSON:
58
61
 
59
62
  ### Hooks
60
63
 
64
+ Execute code each time a new client is created:
65
+
66
+ ```ruby
67
+ G5Updatable::ClientUpdater.on_create do |g5_updatable_client|
68
+ puts "#{g5_updatable_client.name} was just created"
69
+ end
70
+ ```
71
+
61
72
  To execute code every time a location is updated:
62
73
 
63
74
  ```ruby
64
- G5Updatable::LocationsUpdader.on_update do |g5_updatable_location|
75
+ G5Updatable::LocationsUpdater.on_update do |g5_updatable_location|
76
+ puts "G5Updatable::Location##{g5_updatable_location.id} was just updated"
77
+ end
78
+ ```
79
+
80
+ Also optionally expect a secondary block argument, receiving the object previous to be updated
81
+ ... you know.. For diff reasons.
82
+ ```ruby
83
+ G5Updatable::LocationsUpdater.on_update do |g5_updatable_location, before_update_location|
84
+ puts "G5Updatable::Location##{before_update_location.properties} changed to "
85
+ puts "G5Updatable::Location##{g5_updatable_location.properties} "
86
+ end
87
+ ```
88
+
89
+
90
+ or, execute code only when a location is created:
91
+
92
+ ```ruby
93
+ G5Updatable::LocationsUpdater.on_create do |g5_updatable_location|
65
94
  puts "G5Updatable::Location##{g5_updatable_location.id} was just updated"
66
95
  end
67
96
  ```
68
97
 
98
+ The `on_create` callback is called right before the `on_update` callback when a new location is created.
99
+
69
100
  ### Associations
70
101
 
71
102
  You will likely have models in your own application that are associated with a Client or Location. A module is available to include in your related models to support this association. Assuming your model has a `client_uid` or `location_uid` string field respectively, you can do the following:
@@ -4,9 +4,9 @@ module G5Updatable::BelongsToClient
4
4
 
5
5
  included do
6
6
  belongs_to(:client, {
7
- class_name: "G5Updatable::Client",
8
- foreign_key: :client_uid,
9
- primary_key: :uid,
7
+ class_name: 'G5Updatable::Client',
8
+ foreign_key: :client_urn,
9
+ primary_key: :urn,
10
10
  })
11
11
  end
12
12
 
@@ -1,6 +1,6 @@
1
1
  class G5Updatable::FeedController < G5Updatable::BaseUpdatableController
2
2
  def update
3
- G5Updatable::ClientFeedProcessor.new(params[:client_uid]).work
3
+ G5Updatable::ClientFeedProcessor.new(params).work
4
4
  render json: {}, status: :ok
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  class G5Updatable::LocationsController < G5Updatable::BaseUpdatableController
2
2
  def show
3
3
  @location = G5Updatable::Location.find_by_urn(params[:urn])
4
- render json: @location
4
+ render json: { location: @location }
5
5
  end
6
- end
6
+ end
@@ -4,16 +4,16 @@ class G5Updatable::SyncsController < G5Updatable::BaseUpdatableController
4
4
 
5
5
  def index
6
6
  timestamp = @client.locations.max_updated_at
7
- render json: {updated_at: formatted_timestamp(timestamp)}
7
+ render json: { updated_at: formatted_timestamp(timestamp) }
8
8
  end
9
9
 
10
10
  private
11
11
 
12
12
  def set_client
13
13
  @client = G5Updatable::Client.find_by_urn!(params[:urn])
14
- end
14
+ end
15
15
 
16
16
  def formatted_timestamp(timestamp)
17
17
  timestamp.in_time_zone.strftime("%I:%M%P on %B %e, %Y") if timestamp
18
- end
18
+ end
19
19
  end
@@ -4,11 +4,11 @@ module G5Updatable
4
4
  include G5Updatable::UrnAsParameter
5
5
 
6
6
  has_many(:locations, {
7
- class_name: "G5Updatable::Location",
8
- foreign_key: :client_uid,
9
- primary_key: :uid,
7
+ class_name: 'G5Updatable::Location',
8
+ foreign_key: :client_urn,
9
+ primary_key: :urn,
10
10
  })
11
11
 
12
- validates :uid, :urn, presence: true
12
+ validates :uid, :urn, presence: true, uniqueness: true
13
13
  end
14
14
  end
@@ -0,0 +1,6 @@
1
+ module G5Updatable
2
+ class HubAmenitiesLocation < ActiveRecord::Base
3
+ belongs_to :hub_amenity, foreign_key: :g5_updatable_hub_amenity_id
4
+ belongs_to :location, foreign_key: :g5_updatable_location_id
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module G5Updatable
2
+ class HubAmenity < ActiveRecord::Base
3
+ validates :external_id, presence: true
4
+ has_many :hub_amenities_locations, foreign_key: :g5_updatable_hub_amenity_id
5
+
6
+ scope :by_client_urn, -> (client_urn) {
7
+ joins(hub_amenities_locations: { location: :client }).
8
+ where('g5_updatable_clients.urn = :client_urn', client_urn: client_urn).uniq
9
+ }
10
+
11
+ scope :by_client_urns, -> (client_urns) {
12
+ joins(hub_amenities_locations: { location: :client }).
13
+ where('g5_updatable_clients.urn IN (:client_urns)', client_urns: client_urns.join(',')).uniq
14
+ }
15
+
16
+ scope :by_client_uid, -> (client_uid) {
17
+ joins(hub_amenities_locations: :location).
18
+ where('g5_updatable_locations.client_uid = :client_uid', client_uid: client_uid).uniq
19
+ }
20
+ end
21
+ end
@@ -4,18 +4,91 @@ module G5Updatable
4
4
  include G5Updatable::UrnAsParameter
5
5
  include BelongsToClient
6
6
 
7
- validates :uid, :urn, :client_uid, presence: true
7
+ has_many :hub_amenities_locations, foreign_key: :g5_updatable_location_id, dependent: :destroy
8
+ has_many :hub_amenities, through: :hub_amenities_locations
9
+
10
+ validates :uid, :urn, :client_uid, :client_urn, presence: true
11
+ validates :urn, uniqueness: true
8
12
 
9
13
  scope :by_client_uid, -> (client_uid) { where(client_uid: client_uid) }
14
+ scope :by_client_urn, -> (client_urn) { where(client_urn: client_urn) }
15
+ scope :by_postal_code, -> (postal_code) { by_property('postal_code', postal_code) }
16
+ scope :by_city, -> (city) { by_property('city', city) }
17
+ scope :by_state, -> (state) { by_property('state', state) }
18
+ scope :in_status, -> (status) { where("g5_updatable_locations.properties ->> 'status' IN (?)", status) }
19
+ scope :by_neighborhood, -> (neighborhood) {
20
+ where("g5_updatable_locations.properties ->> 'neighborhood' = :neighborhood OR g5_updatable_locations.properties ->> 'neighborhood_2' = :neighborhood", neighborhood: neighborhood)
21
+ }
22
+ scope :by_property, -> (property_name, value) { where("g5_updatable_locations.properties ->> '#{property_name}' = :#{property_name}", property_name.to_sym => value) }
10
23
  scope :by_urn, -> (urn) { where(urn: urn) }
11
24
  scope :max_updated_at, -> { maximum(:updated_at) }
25
+ scope :by_amenity_names, -> (*amenity_names) { # there must be a match for every amenity name (AND not OR)
26
+ results = all
27
+ [amenity_names].flatten.each do |amenity_name|
28
+ results = results.where('g5_updatable_locations.flat_amenity_names LIKE :amenity_names',
29
+ amenity_names: "%#{amenity_names_to_tokenized_string(amenity_name)}%")
30
+ end
31
+ results
32
+ }
33
+ before_validation :set_client_urn, :set_client_uid
34
+
35
+ def refresh_flat_amenity_names!
36
+ update_attributes(flat_amenity_names: self.class.amenity_names_to_tokenized_string(hub_amenities.collect(&:name)))
37
+ end
12
38
 
13
- delegate :urn, to: :client, prefix: true, allow_nil: true
39
+ def amenity_names_array=(names_array)
40
+ self.flat_amenity_names = self.class.amenity_names_to_tokenized_string names_array
41
+ end
42
+
43
+ FLAT_DELIM = '|'
44
+
45
+ def self.amenity_names_to_tokenized_string(names_array)
46
+ return if names_array.blank?
47
+ "#{FLAT_DELIM}#{[names_array].flatten.compact.sort.join(FLAT_DELIM)}#{FLAT_DELIM}"
48
+ end
14
49
 
15
50
  def display_name
16
51
  return nil unless properties
17
52
  return properties['internal_branded_name'] if properties['internal_branded_name'].present?
18
53
  name
19
54
  end
55
+
56
+ def neighborhoods
57
+ [properties['neighborhood'], properties['neighborhood_2']].select(&:present?)
58
+ end
59
+
60
+ def live?
61
+ status?("Live")
62
+ end
63
+
64
+ def no_deploy?
65
+ status?("No Deploy")
66
+ end
67
+
68
+ def pending?
69
+ status?("Pending")
70
+ end
71
+
72
+ def deleted?
73
+ status?("Deleted")
74
+ end
75
+
76
+ def suspended?
77
+ status?("Suspended")
78
+ end
79
+
80
+ def status?(status)
81
+ return false unless properties
82
+ match = properties['status'].to_s.strip =~ /#{status.to_s.strip}/i
83
+ !!match
84
+ end
85
+
86
+ def set_client_urn
87
+ self.client_urn ||= self.client_uid.split('/').last if self.client_uid
88
+ end
89
+
90
+ def set_client_uid
91
+ self.client_uid||= self.client.try(:uid)
92
+ end
20
93
  end
21
94
  end
@@ -9,4 +9,12 @@ class G5Updatable::LocationSerializer < ActiveModel::Serializer
9
9
 
10
10
  keys
11
11
  end
12
+
13
+ def attributes(requested_attrs = nil, reload = false)
14
+ data = super requested_attrs, reload
15
+ object.properties.each do |name, value|
16
+ data[name.to_sym] = value
17
+ end
18
+ data
19
+ end
12
20
  end
@@ -0,0 +1,6 @@
1
+ class AddLatitudeAndLongitudeToLocation < ActiveRecord::Migration
2
+ def change
3
+ add_column :g5_updatable_locations, :latitude, :float
4
+ add_column :g5_updatable_locations, :longitude, :float
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ class CopyLatLongPropsToLatLongColumns < ActiveRecord::Migration
2
+ def up
3
+ G5Updatable::Location.all.each do |location|
4
+ location.latitude = location.properties['latitude']
5
+ location.longitude = location.properties['longitude']
6
+ location.save
7
+ end
8
+ end
9
+
10
+ def down
11
+ G5Updatable::Location.all.each do |location|
12
+ location.properties['latitude'] = location.latitude
13
+ location.proper['longitude'] = location.longitude
14
+ location.latitude = nil
15
+ location.longitude = nil
16
+ location.save
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ class AddLatitudeLongitudeIndexesToLocation < ActiveRecord::Migration
2
+ def change
3
+ add_index :g5_updatable_locations, :latitude
4
+ add_index :g5_updatable_locations, :longitude
5
+ end
6
+ end
@@ -0,0 +1,25 @@
1
+ class AddAmenities < ActiveRecord::Migration
2
+ def change
3
+ create_table :g5_updatable_hub_amenities do |t|
4
+ t.integer :external_id
5
+ t.string :name
6
+ t.string :icon
7
+ t.timestamp :external_updated_at
8
+ t.timestamp :external_created_at
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :g5_updatable_hub_amenities, :external_id, unique: true
13
+
14
+ create_table :g5_updatable_hub_amenities_locations do |t|
15
+ t.belongs_to :g5_updatable_hub_amenity
16
+ t.belongs_to :g5_updatable_location
17
+ end
18
+
19
+ add_index :g5_updatable_hub_amenities_locations, :g5_updatable_hub_amenity_id, name: 'updatable_amenities_loc_amen_id'
20
+ add_index :g5_updatable_hub_amenities_locations, :g5_updatable_location_id, name: 'updatable_amenities_loc_loc_id'
21
+
22
+ # we need this for queries that require a location to have ALL amenities in a list
23
+ add_column :g5_updatable_locations, :flat_amenity_names, :string
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ class AddClientUrnToLocations < ActiveRecord::Migration
2
+ def change
3
+ add_column :g5_updatable_locations, :client_urn, :string
4
+ add_index :g5_updatable_locations, :client_urn
5
+ end
6
+ end
data/lib/g5_updatable.rb CHANGED
@@ -1,6 +1,8 @@
1
- require "active_model_serializers"
2
- require "g5_foundation_client"
3
- require "g5_updatable/engine"
1
+ require 'active_model_serializers'
2
+ require 'g5_authentication_client'
3
+ require 'g5_updatable/engine'
4
+ require 'httparty'
4
5
 
5
6
  module G5Updatable
7
+ HUB_URL = ENV.fetch('HUB_URL', 'https://g5-hub.herokuapp.com').freeze
6
8
  end
@@ -0,0 +1,17 @@
1
+ module G5Updatable
2
+ class AllClientUrnsFetcher
3
+ def self.fetch_uids
4
+ G5Updatable::Fetcher.get_with_token(url)['clients'].collect do |urn|
5
+ "#{hub_url}/clients/#{urn['urn']}.json"
6
+ end
7
+ end
8
+
9
+ def self.url
10
+ "#{hub_url}/urns.json"
11
+ end
12
+
13
+ def self.hub_url
14
+ G5Updatable::HUB_URL
15
+ end
16
+ end
17
+ end
@@ -1,37 +1,49 @@
1
1
  class G5Updatable::ClientFeedProcessor
2
2
  attr_reader :client_uid
3
3
 
4
- def initialize(client_uid=nil)
5
- @client_uid = client_uid || ENV["CLIENT_UID"]
4
+ def initialize(params)
5
+ @client_uid = params[:client_uid] || ENV["CLIENT_UID"]
6
6
  raise "A client_uid must be either passed in or configured!" if @client_uid.blank?
7
+ @location_uid = params[:location_uid]
7
8
  end
8
9
 
9
10
  class << self
10
- def load_all_clients(clients_url)
11
- client_uids = G5FoundationClient::Client.all_client_uids clients_url
12
- client_uids.collect { |client_uid| new(client_uid).work }
11
+ def load_all_clients
12
+ G5Updatable::AllClientUrnsFetcher.fetch_uids.each do |uid|
13
+ puts "Loading: #{uid}"
14
+ load_client(uid)
15
+ end
16
+ end
17
+
18
+ def load_client(client_uid)
19
+ begin
20
+ new(client_uid: client_uid).work
21
+ rescue => e
22
+ Rails.logger.error e
23
+ nil
24
+ end
13
25
  end
14
26
  end
15
27
 
16
28
  def work
17
- client = update_client
18
- update_locations
19
-
20
- client
29
+ @location_uid ? update_location : update_client
21
30
  end
22
31
 
23
32
  private
24
33
 
25
- def update_locations
26
- G5Updatable::LocationsUpdater.new(foundation_client).update
34
+ def update_location
35
+ G5Updatable::LocationsUpdater.new(location_hash).update
27
36
  end
28
37
 
29
38
  def update_client
30
- G5Updatable::ClientUpdater.new(foundation_client).update
39
+ G5Updatable::ClientUpdater.new(client_hash).update
31
40
  end
32
41
 
33
- def foundation_client
34
- @foundation_client ||= G5FoundationClient::Client.find_by_uid(@client_uid)
42
+ def client_hash
43
+ G5Updatable::Fetcher.get_with_token(@client_uid)['client']
35
44
  end
36
45
 
46
+ def location_hash
47
+ G5Updatable::Fetcher.get_with_token(@location_uid)['location']
48
+ end
37
49
  end