g5_updatable 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -34
  3. data/app/concerns/g5_updatable/belongs_to_location.rb +14 -0
  4. data/app/concerns/g5_updatable/first_class_properties.rb +25 -0
  5. data/app/concerns/g5_updatable/urn_as_parameter.rb +7 -0
  6. data/app/controllers/g5_updatable/feed_controller.rb +1 -1
  7. data/app/models/g5_updatable/client.rb +12 -0
  8. data/app/models/g5_updatable/integration_setting.rb +20 -0
  9. data/app/models/g5_updatable/location.rb +16 -0
  10. data/app/serializers/g5_updatable/location_serializer.rb +12 -0
  11. data/db/migrate/20140709222005_create_g5_updatable_clients_and_locations.rb +24 -0
  12. data/db/migrate/20141030211945_create_integration_setting.rb +18 -0
  13. data/lib/g5_updatable/client_feed_processor.rb +34 -12
  14. data/lib/g5_updatable/client_updater.rb +7 -16
  15. data/lib/g5_updatable/engine.rb +2 -13
  16. data/lib/g5_updatable/integration_settings_updater.rb +21 -0
  17. data/lib/g5_updatable/locations_updater.rb +14 -21
  18. data/lib/g5_updatable/rspec/factories.rb +19 -0
  19. data/lib/g5_updatable/rspec.rb +3 -0
  20. data/lib/g5_updatable/version.rb +1 -1
  21. data/lib/g5_updatable.rb +2 -1
  22. data/lib/generators/g5_updatable/install/USAGE +0 -3
  23. data/lib/generators/g5_updatable/install/install_generator.rb +0 -6
  24. data/lib/tasks/g5_updatable_tasks.rake +12 -4
  25. data/spec/concerns/g5_updatable/belongs_to_location_spec.rb +30 -0
  26. data/spec/dummy/app/models/favorite_food.rb +3 -0
  27. data/spec/dummy/config/database.sample.yml +9 -0
  28. data/spec/dummy/config/database.travis.yml +4 -0
  29. data/spec/dummy/db/migrate/20140709220627_drop_clients_and_locations.rb +10 -0
  30. data/spec/dummy/db/migrate/20140714225203_create_favorite_foods.rb +10 -0
  31. data/spec/dummy/db/schema.rb +39 -21
  32. data/spec/dummy/log/test.log +18589 -920
  33. data/spec/lib/g5_updatable/client_feed_processor_spec.rb +77 -22
  34. data/spec/lib/g5_updatable/client_updater_spec.rb +28 -41
  35. data/spec/lib/g5_updatable/integration_settings_updater_spec.rb +48 -0
  36. data/spec/lib/g5_updatable/locations_updater_spec.rb +29 -52
  37. data/spec/lib/generators/g5_updatable/install_generator_spec.rb +0 -12
  38. data/spec/models/g5_updatable/client_spec.rb +25 -0
  39. data/spec/models/g5_updatable/integration_setting_spec.rb +33 -0
  40. data/spec/models/g5_updatable/location_spec.rb +34 -0
  41. data/spec/serializers/g5_updatable/location_serializer_spec.rb +21 -0
  42. data/spec/spec_helper.rb +13 -5
  43. data/spec/support/shared_example_for_urn_as_parameter.rb +7 -0
  44. data/spec/support/shared_examples_for_first_class_properties_json.rb +29 -0
  45. metadata +104 -42
  46. data/lib/g5_updatable/feed_mapper.rb +0 -58
  47. data/lib/g5_updatable/g5_client.rb +0 -10
  48. data/lib/g5_updatable/g5_location.rb +0 -22
  49. data/lib/generators/g5_updatable/install/templates/g5_updatable.rb +0 -25
  50. data/spec/dummy/config/database.yml +0 -25
  51. data/spec/dummy/config/initializers/g5_updatable.rb +0 -4
  52. data/spec/dummy/db/development.sqlite3 +0 -0
  53. data/spec/dummy/log/development.log +0 -6434
  54. data/spec/dummy/spec/fabricators/client_fabricator.rb +0 -6
  55. data/spec/dummy/spec/fabricators/location_fabricator.rb +0 -9
  56. data/spec/dummy/spec/models/client_spec.rb +0 -0
  57. data/spec/dummy/spec/models/location_spec.rb +0 -0
  58. data/spec/dummy/spec/support/client_feed.html +0 -97
  59. data/spec/dummy/spec/support/updated_client_feed.html +0 -148
  60. data/spec/fabricators/client_fabricator.rb +0 -6
  61. data/spec/fabricators/location_fabricator.rb +0 -9
  62. data/spec/lib/g5_updatable/feed_mapper_spec.rb +0 -119
  63. data/spec/lib/tmp/config/initializers/g5_updatable.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bde0a7d86b404b9ac963b31247542ba845666eeb
4
- data.tar.gz: d0b7a782024e35fd093883883979078370ef1db6
3
+ metadata.gz: 3a07cbb7571a2a542b0ee33d002767be1a2a55f7
4
+ data.tar.gz: 03a45dc270a2144190f1b8d75c9790639e3dfea7
5
5
  SHA512:
6
- metadata.gz: a8024e7f5fd6aa37805eac7b87e11d4cffd2f7802286de63709761eeac71ff86056ebdf517f4ba13b08b3e18e0e538d7ee819d6f5717013e16db7c1ae28192f8
7
- data.tar.gz: 76c110e7d08fa1ea6c1608e081ddb8673ba0743d432d3f89ba7d1596bb0991f8ffcc30a3e40813511d8d5d7704c64a404872081b85a804c314319d1ed00f49cb
6
+ metadata.gz: 1eddbf5326ca2ed281892b39457f5b9c79b3dccb2689c898aeeca03107dbcbe5cee213f6cebbd922d316640445dba98d0ddcb0612137aee606c212a8450db8b6
7
+ data.tar.gz: 55b69f64376dae0bc537a0b62f0e17b4ed1e754fac7809eb50fff6f8910a79153e2fcdcb1bf349ead4ff13a9a8ce60b69b984eeb8178a5b993c8fd6b862f5dad
data/README.md CHANGED
@@ -6,6 +6,10 @@
6
6
  G5 Updatable provides a solution for automatic updates of client and location
7
7
  data when modified or created in the G5 Hub.
8
8
 
9
+ ## Requirements
10
+
11
+ G5 Updatable makes use of PostgrSQL's `json` field type, and so only supports implementing apps that also use PostgreSQL.
12
+
9
13
  ## Installation
10
14
 
11
15
  1. Add this line to your application's Gemfile:
@@ -19,56 +23,53 @@ data when modified or created in the G5 Hub.
19
23
  ```console
20
24
  bundle
21
25
  ```
22
-
23
26
  3. Run the generator.
24
27
 
25
28
  ```ruby
26
29
  rails g g5_updatable:install
27
30
  ```
28
31
 
29
- This creates an initilizer at config/initializers/g5_updatable.rb,
30
- and mounts the engine at `/g5_updatable`.
32
+ This mounts the engine at `/g5_updatable`.
33
+
34
+ 3. And copy the engine's migrations to your application:
35
+
36
+ ```console
37
+ rake g5_updatable:install:migrations
38
+ ```
39
+ 3. Optional: load all of G5-Hub's data into your database
40
+
41
+ ```console
42
+ rake g5_updatable:load_all
43
+ ```
44
+ Note, all of the G5_AUTH env variables need to be set for this to work.
45
+
46
+ ## Usage
47
+
48
+ G5 Updatable exposes a POST route at `/g5_updatable/update` that accepts a
49
+ `client_uid` parameter (the URL for the client's detail page within the G5
50
+ Hub). When the route is hit, it will update/create Location and Client.
31
51
 
32
- ## Configuration
52
+ See the [G5-Hub](https://github.com/G5/g5-hub/blob/master/lib/webhook_poster.rb) webhook logic for further info.
33
53
 
34
- You can configure options within the generated initializer.
54
+ ### Location Association
55
+
56
+ You will likely have models in your own application that are associated with a Location. A module is available to include in your related models to support this association. Assuming your model has a `location_uid` string field, you can use the module as follows:
35
57
 
36
58
  ```ruby
37
- # config/initilizers/g5_updatable.rb
38
- G5Updatable.setup do |config|
39
- # Default is nil. Most likely will be coming in via the hub urn parameter.
40
- # dentifier of the client (urn)
41
- config.client_identifier = #string
42
-
43
- # Default is ""http://g5-hub.herokuapp.com/clients/". Base path to the G5 Hub
44
- config.feed_endpoint = #string
45
-
46
- # default is true. When set to true, existing locations in your app will be
47
- # updated with any changes made to the hub. If set to false, existing locations
48
- # will be skipped and only newly added locations will be created.
49
- config.update_locations = #boolean
50
-
51
- # default is false. When set to true, client data will update.
52
- config.update_client = #boolean
53
-
54
- # default is [:name]. A whitlist of parameters to create/update on the model
55
- config.location_parameters = #array of symbols
56
-
57
- # default is [:name]. A whitlist of parameters to update on the model
58
- config.client_parameters = #array of symbols
59
+ class Foo < ActiveRecord::Base
60
+ include G5Updatable::BelongsToLocation
59
61
  end
60
62
  ```
61
63
 
62
- ## Usage
64
+ It provides a `#location` method that will fetch the correct location.
63
65
 
64
- G5 Updatable exposes a POST route at `/g5_updatable/update` that accepts a urn
65
- parameter (client identifier within the hub). When the route is
66
- hit, it will update/create Location and Client data based on the configuration.
66
+ ### Spec Helpers
67
67
 
68
- G5 Updatable assumes your app has a Location model with a minimum of a urn and
69
- name column.
68
+ The engine provides a helper files that can be included in your project to bring in some testing support. Currently this is limited to (some factory definitions)[https://github.com/G5/g5_updatable/blob/active-record-up-in-here/lib/g5_updatable/rspec/factories.rb]. In rspec you can add the following line to your `spec/spec_helper.rb`:
70
69
 
71
- See the [G5-Hub](https://github.com/G5/g5_hub/lib/webhook_poster.rb) webhook logic for further info.
70
+ ```ruby
71
+ require 'g5_updatable/rspec'
72
+ ```
72
73
 
73
74
  ## Authors
74
75
 
@@ -88,6 +89,16 @@ If you find bugs, have feature requests or questions, please
88
89
 
89
90
  ## Specs
90
91
 
92
+ The `database.yml` for the dummy app must be created and modified to match your
93
+ PostgreSQL configuration. If you are using the [G5 Orion
94
+ Vagrant](https://github.com/G5/g5-orion-vagrant) image, the sample file should
95
+ just work. You can copy it into place with:
96
+ ```bash
97
+ $ cp spec/dummy/config/database.sample.yml spec/dummy/config/database.yml
98
+ ```
99
+
100
+ Run specs via `rspec` with:
101
+
91
102
  ```bash
92
103
  $ rspec spec
93
104
  ```
@@ -0,0 +1,14 @@
1
+ module G5Updatable::BelongsToLocation
2
+ extend ActiveSupport::Concern
3
+
4
+ def location_uid=(location_uid)
5
+ @location = nil
6
+ write_attribute(:location_uid, location_uid)
7
+ end
8
+
9
+ def location
10
+ @location ||= G5Updatable::Location.find_by_uid!(location_uid)
11
+ rescue ActiveRecord::RecordNotFound
12
+ raise ActiveRecord::RecordNotFound.new("Can't find a location for uid '#{location_uid}'")
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ module G5Updatable::FirstClassProperties
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ after_initialize :define_methods_for_properties
6
+ end
7
+
8
+ def properties=(hash)
9
+ write_attribute("properties", hash)
10
+ define_methods_for_properties
11
+ end
12
+
13
+ protected
14
+
15
+ def define_methods_for_properties
16
+ return unless properties.present?
17
+
18
+ properties.each do |key, value|
19
+ next if respond_to?(key)
20
+ define_singleton_method(key) do
21
+ properties[key.to_s] if properties.present?
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ module G5Updatable::UrnAsParameter
2
+ extend ActiveSupport::Concern
3
+
4
+ def to_param
5
+ urn
6
+ end
7
+ end
@@ -3,7 +3,7 @@ class G5Updatable::FeedController < ApplicationController
3
3
  skip_before_filter :verify_authenticity_token
4
4
 
5
5
  def update
6
- G5Updatable::ClientFeedProcessor.new(params[:urn]).work
6
+ G5Updatable::ClientFeedProcessor.new(params[:client_uid]).work
7
7
  render json: {}, status: :ok
8
8
  end
9
9
  end
@@ -0,0 +1,12 @@
1
+ module G5Updatable
2
+ class Client < ActiveRecord::Base
3
+ include G5Updatable::FirstClassProperties
4
+ include G5Updatable::UrnAsParameter
5
+
6
+ validates :uid, :urn, presence: true
7
+
8
+ def locations
9
+ G5Updatable::Location.where(client_uid: self.uid)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module G5Updatable
2
+ class IntegrationSetting < ActiveRecord::Base
3
+ include G5Updatable::FirstClassProperties
4
+ include G5Updatable::UrnAsParameter
5
+
6
+ validates :uid, presence: true
7
+ validates :location_uid, presence: true
8
+
9
+ INVENTORY = 'inventory'
10
+ LEAD = 'lead'
11
+
12
+ scope :by_vendor_action, -> (vendor_action) { where(vendor_action: vendor_action) }
13
+ scope :by_inventory, -> { by_vendor_action(INVENTORY) }
14
+ scope :by_lead, -> { by_vendor_action(LEAD) }
15
+
16
+ def location
17
+ @client ||= G5Updatable::Location.find_by_uid(location_uid)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module G5Updatable
2
+ class Location < ActiveRecord::Base
3
+ include G5Updatable::FirstClassProperties
4
+ include G5Updatable::UrnAsParameter
5
+
6
+ validates :uid, :urn, :client_uid, presence: true
7
+
8
+ def client
9
+ @client ||= G5Updatable::Client.find_by_uid(client_uid)
10
+ end
11
+
12
+ def integration_settings
13
+ G5Updatable::IntegrationSetting.where(location_uid: self.uid)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ class G5Updatable::LocationSerializer < ActiveModel::Serializer
2
+ attributes :uid, :urn, :client_uid
3
+
4
+ def filter(keys)
5
+ object.properties.each do |name, value|
6
+ keys.push(name.to_sym)
7
+ define_singleton_method(name.to_sym) { value }
8
+ end
9
+
10
+ keys
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ class CreateG5UpdatableClientsAndLocations < ActiveRecord::Migration
2
+ def change
3
+ create_table :g5_updatable_clients do |t|
4
+ t.string :uid
5
+ t.string :urn
6
+ t.json :properties
7
+
8
+ t.timestamps
9
+ end
10
+ add_index :g5_updatable_clients, :uid
11
+ add_index :g5_updatable_clients, :urn
12
+
13
+ create_table :g5_updatable_locations do |t|
14
+ t.string :uid
15
+ t.string :urn
16
+ t.string :client_uid
17
+ t.json :properties
18
+
19
+ t.timestamps
20
+ end
21
+ add_index :g5_updatable_locations, :uid
22
+ add_index :g5_updatable_locations, :urn
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ class CreateIntegrationSetting < ActiveRecord::Migration
2
+ def change
3
+ create_table :g5_updatable_integration_settings do |t|
4
+ t.string :uid
5
+ t.string :urn
6
+ t.string :location_uid
7
+ t.string :vendor_action
8
+ t.integer :job_frequency_in_minutes
9
+ t.json :properties
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :g5_updatable_integration_settings, :urn
14
+ add_index :g5_updatable_integration_settings, :uid
15
+ add_index :g5_updatable_integration_settings, :vendor_action
16
+ add_index :g5_updatable_integration_settings, [:location_uid, :vendor_action], name: :g5_u_is_loc_action
17
+ end
18
+ end
@@ -1,22 +1,44 @@
1
- require "g5_updatable/feed_mapper"
2
- require "g5_updatable/client_updater"
3
- require "g5_updatable/locations_updater"
4
-
5
1
  class G5Updatable::ClientFeedProcessor
6
- def initialize(urn=nil)
7
- @urn = urn || G5Updatable.client_identifier
2
+ attr_reader :client_uid
3
+
4
+ def initialize(client_uid=nil)
5
+ @client_uid = client_uid || ENV["CLIENT_UID"]
6
+ raise "A client_uid must be either passed in or configured!" if @client_uid.blank?
7
+ end
8
+
9
+ 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 }
13
+ end
8
14
  end
9
15
 
10
16
  def work
11
- if @urn
12
- G5Updatable::ClientUpdater.new(feed_mapper.client).update
13
- G5Updatable::LocationsUpdater.new(feed_mapper.locations).update
17
+ client = update_client
18
+ update_locations
19
+ update_integrations
20
+
21
+ client
22
+ end
23
+
24
+ private
25
+
26
+ def update_integrations
27
+ foundation_client.locations.each do |location|
28
+ G5Updatable::IntegrationSettingsUpdater.new(location.integration_settings).update
14
29
  end
15
30
  end
16
31
 
17
- private
32
+ def update_locations
33
+ G5Updatable::LocationsUpdater.new(foundation_client.locations).update
34
+ end
35
+
36
+ def update_client
37
+ G5Updatable::ClientUpdater.new(foundation_client).update
38
+ end
18
39
 
19
- def feed_mapper
20
- @feed_mapper ||= G5Updatable::FeedMapper.new(@urn)
40
+ def foundation_client
41
+ @foundation_client ||= G5FoundationClient::Client.find_by_uid(@client_uid)
21
42
  end
43
+
22
44
  end
@@ -4,22 +4,13 @@ class G5Updatable::ClientUpdater
4
4
  end
5
5
 
6
6
  def update
7
- return unless G5Updatable.update_client && client
7
+ attributes = @g5_client.client_hash.dup
8
+ attributes.delete(:locations)
9
+ uid = attributes[:uid]
10
+ urn = attributes[:urn]
8
11
 
9
- G5Updatable.client_parameters.each do |parameter|
10
- client.send("#{parameter}=", @g5_client.send(parameter))
11
- end
12
-
13
- client.save
14
- end
15
-
16
- private
17
-
18
- def client
19
- @client ||= Client.find_by(uid: client_uid) if client_uid
20
- end
21
-
22
- def client_uid
23
- "#{G5Updatable.feed_endpoint}#{G5Updatable.client_identifier}"
12
+ G5Updatable::Client.
13
+ find_or_initialize_by(uid: uid).
14
+ update_attributes!(urn: urn, properties: attributes)
24
15
  end
25
16
  end
@@ -2,26 +2,15 @@ module G5Updatable
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace G5Updatable
4
4
 
5
+ config.autoload_paths << G5Updatable::Engine.root.join("lib")
6
+
5
7
  config.generators do |g|
6
8
  g.test_framework :rspec
7
- g.fixture_replacement :fabrication, dir: 'spec/fabricators'
8
9
  g.assets false
9
10
  g.helper false
10
11
  end
11
12
  end
12
13
 
13
- class << self
14
- mattr_accessor :client_identifier, :feed_endpoint, :location_parameters,
15
- :client_parameters, :update_client, :update_locations
16
-
17
- self.client_identifier = nil
18
- self.feed_endpoint = "http://g5-hub.herokuapp.com/clients/"
19
- self.location_parameters = [:name]
20
- self.client_parameters = [:name]
21
- self.update_client = false
22
- self.update_locations = true
23
- end
24
-
25
14
  def self.setup(&block)
26
15
  yield self
27
16
  end
@@ -0,0 +1,21 @@
1
+ class G5Updatable::IntegrationSettingsUpdater
2
+ def initialize(g5_integration_settings)
3
+ @g5_integration_settings = g5_integration_settings
4
+ end
5
+
6
+ def update
7
+ @g5_integration_settings.each do |g5_integration_setting|
8
+ attributes = g5_integration_setting.integration_setting_hash.dup
9
+
10
+ G5Updatable::IntegrationSetting.
11
+ find_or_initialize_by(uid: attributes[:uid]).
12
+ update_attributes!(
13
+ urn: attributes[:urn],
14
+ location_uid: attributes[:location_uid],
15
+ vendor_action: attributes[:vendor_action],
16
+ job_frequency_in_minutes: attributes[:job_frequency_in_minutes],
17
+ properties: attributes
18
+ )
19
+ end
20
+ end
21
+ end
@@ -1,29 +1,22 @@
1
1
  class G5Updatable::LocationsUpdater
2
- def initialize(locations)
3
- @locations = locations
2
+ def initialize(g5_locations)
3
+ @g5_locations = g5_locations
4
4
  end
5
5
 
6
6
  def update
7
- @locations.each { |location| process(location) unless skip?(location) }
8
- end
9
-
10
- private
7
+ @g5_locations.each do |g5_location|
8
+ attributes = g5_location.location_hash.dup
9
+ uid = attributes[:uid]
10
+ urn = attributes[:urn]
11
+ client_uid = attributes[:client_uid]
11
12
 
12
- def process(g5_location)
13
- location = Location.find_or_initialize_by(urn: urn_for(g5_location))
14
-
15
- G5Updatable.location_parameters.each do |parameter|
16
- location.send("#{parameter}=", g5_location.send(parameter))
13
+ G5Updatable::Location.
14
+ find_or_initialize_by(uid: uid).
15
+ update_attributes!(
16
+ urn: urn,
17
+ client_uid: client_uid,
18
+ properties: attributes
19
+ )
17
20
  end
18
-
19
- location.save
20
- end
21
-
22
- def skip?(location)
23
- Location.exists?(urn: urn_for(location)) && !G5Updatable.update_locations
24
- end
25
-
26
- def urn_for(location)
27
- location.uid.to_s.split("/").last
28
21
  end
29
22
  end