wcc-data 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +3 -0
  3. data/.gitignore +18 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +29 -0
  8. data/Rakefile +36 -0
  9. data/circle.yml +6 -0
  10. data/lib/wcc/data.rb +26 -0
  11. data/lib/wcc/data/config.rb +45 -0
  12. data/lib/wcc/data/enumerated_type.rb +63 -0
  13. data/lib/wcc/data/faraday_client_app_token_auth.rb +64 -0
  14. data/lib/wcc/data/mapper.rb +16 -0
  15. data/lib/wcc/data/mapper/attributes.rb +54 -0
  16. data/lib/wcc/data/mapper/json_response.rb +20 -0
  17. data/lib/wcc/data/mapper/rest_configuration.rb +36 -0
  18. data/lib/wcc/data/mapper/rest_query.rb +28 -0
  19. data/lib/wcc/data/model.rb +11 -0
  20. data/lib/wcc/data/nucleus.rb +15 -0
  21. data/lib/wcc/data/nucleus/address.rb +17 -0
  22. data/lib/wcc/data/nucleus/base.rb +6 -0
  23. data/lib/wcc/data/nucleus/campus.rb +47 -0
  24. data/lib/wcc/data/nucleus/client_app_token.rb +30 -0
  25. data/lib/wcc/data/nucleus/contact.rb +33 -0
  26. data/lib/wcc/data/nucleus/gender.rb +26 -0
  27. data/lib/wcc/data/nucleus/group.rb +18 -0
  28. data/lib/wcc/data/nucleus/marital_status.rb +41 -0
  29. data/lib/wcc/data/nucleus/user.rb +49 -0
  30. data/lib/wcc/data/rack_client_app_token_auth.rb +77 -0
  31. data/lib/wcc/data/response.rb +29 -0
  32. data/lib/wcc/data/rest_endpoint.rb +33 -0
  33. data/lib/wcc/data/service.rb +55 -0
  34. data/lib/wcc/data/version.rb +5 -0
  35. data/spec/spec_helper.rb +22 -0
  36. data/spec/support/inheritable_class_attribute_examples.rb +16 -0
  37. data/spec/wcc/data/config_spec.rb +113 -0
  38. data/spec/wcc/data/enumerated_type_spec.rb +176 -0
  39. data/spec/wcc/data/faraday_client_app_token_auth_spec.rb +224 -0
  40. data/spec/wcc/data/mapper/attributes_spec.rb +94 -0
  41. data/spec/wcc/data/mapper/json_response_spec.rb +50 -0
  42. data/spec/wcc/data/mapper/rest_configuration_spec.rb +43 -0
  43. data/spec/wcc/data/mapper/rest_query_spec.rb +50 -0
  44. data/spec/wcc/data/model_spec.rb +33 -0
  45. data/spec/wcc/data/nucleus/campus_spec.rb +29 -0
  46. data/spec/wcc/data/rack_client_app_token_auth_spec.rb +219 -0
  47. data/spec/wcc/data/response_spec.rb +57 -0
  48. data/spec/wcc/data/rest_endpoint_spec.rb +71 -0
  49. data/spec/wcc/data/service_spec.rb +128 -0
  50. data/spec/wcc/data_spec.rb +25 -0
  51. data/wcc-data.gemspec +28 -0
  52. metadata +194 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a6ff0edbb218a416c3885bf1e8a028be54c8dfd6
4
+ data.tar.gz: 8304efad93290668fc1ae9022fcd1f4d90944989
5
+ SHA512:
6
+ metadata.gz: bbc162507df12d2389a32348cf99ae1335345af81f5b12d919a0b7707159f586df1f1291887e1831e3b2e17dd4a95bcfea23ee0220f4ef51220c3ce8827bb9e6
7
+ data.tar.gz: 4f9fb68b42ccd76f8dec94a6fb6f97dd10c97a766529ef19c70a456a0bdb344a7076ff9fed15269d7071a929a35412d8747e6279e62fbb6f48cbfa9beb92acdb
data/.env.example ADDED
@@ -0,0 +1,3 @@
1
+ NUCLEUS_CLIENT_ID=client-id-3
2
+ NUCLEUS_CLIENT_SECRET=client-secret-3
3
+ NUCLEUS_URL=http://login.dev/
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .env
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wcc-data.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Travis Petticrew
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Wcc::Data
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'wcc-data'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install wcc-data
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'dotenv/tasks'
3
+
4
+ PROJECT_PATH = File.dirname(__FILE__)
5
+ LIB_PATH = File.join(PROJECT_PATH, "lib")
6
+
7
+ task :default => :test
8
+
9
+ desc 'run test examples'
10
+ task :test do
11
+ exec 'rspec .'
12
+ end
13
+
14
+ task :environment => :dotenv do
15
+ $LOAD_PATH.unshift(LIB_PATH)
16
+ require 'wcc/data'
17
+ end
18
+
19
+ task :development => [:environment] do
20
+ WCC::Data.setup do |config|
21
+ conn = config.applications[:nucleus].connection
22
+ conn.use Faraday::Response::Logger
23
+ conn.basic_auth(ENV['NUCLEUS_CLIENT_ID'], ENV['NUCLEUS_CLIENT_SECRET'])
24
+ end
25
+ end
26
+
27
+ desc 'launch IRB shell with base environment loaded'
28
+ task :irb => :environment do
29
+ require 'irb'
30
+ ARGV.clear
31
+ IRB.start
32
+ end
33
+
34
+ desc 'launch IRB shell with development environment loaded'
35
+ task :console => [:development, :irb]
36
+
data/circle.yml ADDED
@@ -0,0 +1,6 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.0
4
+ dependencies:
5
+ pre:
6
+ - cp .env.example .env
data/lib/wcc/data.rb ADDED
@@ -0,0 +1,26 @@
1
+ require_relative "data/version"
2
+
3
+ module WCC::Data
4
+
5
+ def self.config
6
+ @config ||= WCC::Data::Config.new
7
+ end
8
+
9
+ def self.setup
10
+ yield config
11
+ end
12
+
13
+ end
14
+
15
+ require_relative "data/config"
16
+ require_relative "data/enumerated_type"
17
+ require_relative "data/faraday_client_app_token_auth"
18
+ require_relative "data/mapper"
19
+ require_relative "data/model"
20
+ require_relative "data/rack_client_app_token_auth"
21
+ require_relative "data/response"
22
+ require_relative "data/rest_endpoint"
23
+ require_relative "data/service"
24
+
25
+ require_relative "data/nucleus"
26
+
@@ -0,0 +1,45 @@
1
+ require 'uri'
2
+ require 'faraday'
3
+
4
+ module WCC::Data
5
+ class Config
6
+ def initialize
7
+ set_default_applications
8
+ end
9
+
10
+ def applications
11
+ @applications ||= Hash.new { |hash, key| hash[key] = Application.new(key) }
12
+ end
13
+ alias :apps :applications
14
+
15
+ private
16
+
17
+ def set_default_applications
18
+ applications[:nucleus].uri = ENV['NUCLEUS_URL']
19
+ if ENV['APP_CLIENT_ID'] && ENV['APP_CLIENT_SECRET']
20
+ applications[:nucleus].connection
21
+ .basic_auth(ENV['APP_CLIENT_ID'], ENV['APP_CLIENT_SECRET'])
22
+ end
23
+ end
24
+
25
+ class Application
26
+ attr_reader :name, :uri
27
+ attr_accessor :connection
28
+
29
+ def initialize(name, connection: Faraday::Connection.new)
30
+ @name = name
31
+ @connection = connection
32
+ end
33
+
34
+ def uri=(uri)
35
+ @uri = URI(uri)
36
+ end
37
+
38
+ def service
39
+ Service.new(connection: connection, uri: uri)
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,63 @@
1
+ module WCC::Data
2
+ class EnumeratedType
3
+
4
+ def initialize(args={})
5
+ only_attributes(args).each do |key, value|
6
+ instance_variable_set("@#{key}", value)
7
+ end
8
+ end
9
+
10
+ def self.[](value)
11
+ all.find { |record| record.matches?(value) }
12
+ end
13
+
14
+ def matches?(value)
15
+ raise NotImplementedError,
16
+ "The #matches? method should be defined in subclasses and return " \
17
+ "true if the argument matches this record and false otherwise."
18
+ end
19
+
20
+ def self.db
21
+ raise NotImplementedError,
22
+ "The ::db class method should be defined in subclasses as an array " \
23
+ "of hashes containing data for each record."
24
+ end
25
+
26
+ def self.all
27
+ @all ||= db.collect { |data| new(data) }
28
+ end
29
+
30
+ def self.reset
31
+ @all = nil
32
+ end
33
+
34
+ private
35
+
36
+ def only_attributes(args)
37
+ args.select { |key, value|
38
+ self.class.defined_attributes.include?(key)
39
+ }
40
+ end
41
+
42
+ def self.attributes(*attrs)
43
+ attrs.each do |attr|
44
+ defined_attributes << attr
45
+ define_method(attr) do
46
+ instance_variable_get("@#{attr}")
47
+ end
48
+ end
49
+ end
50
+
51
+ def self.defined_attributes
52
+ @attributes ||= []
53
+ end
54
+
55
+ def self.inherited(subclass)
56
+ subclass.send(
57
+ :instance_variable_set,
58
+ :@attributes,
59
+ defined_attributes.dup
60
+ )
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,64 @@
1
+ module WCC::Data
2
+ class FaradayClientAppTokenAuth < Faraday::Middleware
3
+ class RedisCache
4
+ DEFAULT_CACHE_KEY = "org.watermark.data.client-app-token-cache-store"
5
+ attr_reader :connection, :cache_key
6
+
7
+ def initialize(connection, cache_key: DEFAULT_CACHE_KEY)
8
+ @connection = connection
9
+ @cache_key = cache_key
10
+ end
11
+
12
+ def [](hostname)
13
+ connection.call { |r| r.hget cache_key, hostname }
14
+ end
15
+
16
+ def []=(hostname, token)
17
+ if token
18
+ connection.call { |r| r.hset cache_key, hostname, token }
19
+ else
20
+ connection.call { |r| r.hdel cache_key, hostname }
21
+ end
22
+ end
23
+ end
24
+
25
+ attr_reader :cache, :token_factory
26
+
27
+ def initialize(app, cache: default_cache, token_factory: method(:default_token_factory))
28
+ super(app)
29
+ @cache = cache
30
+ @token_factory = token_factory
31
+ end
32
+
33
+ def token_for(host)
34
+ @cache[host] ||= token_factory.(host)
35
+ end
36
+
37
+ def call(env, retry_on_forbidden: true)
38
+ request_body = env[:body]
39
+ host = env[:url].host
40
+ env[:request_headers]["Authorization"] = "Bearer #{token_for(host)}"
41
+ @app.call(env).on_complete do |response_env|
42
+ response = Faraday::Response.new(response_env)
43
+ cache[host] = nil unless response.success?
44
+ if response.status == 403 && retry_on_forbidden
45
+ env[:body] = request_body
46
+ call(env, retry_on_forbidden: false)
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def default_token_factory(host)
54
+ WCC::Data::Nucleus::ClientAppToken.create(hostname: host).token
55
+ end
56
+
57
+ def default_cache
58
+ RedisCache.new(Sidekiq.method(:redis))
59
+ end
60
+ end
61
+ end
62
+
63
+ Faraday::Middleware.register_middleware :client_app_token => lambda { WCC::Data::FaradayClientAppTokenAuth }
64
+
@@ -0,0 +1,16 @@
1
+ module WCC::Data
2
+
3
+ module Mapper
4
+ class MapperError < StandardError; end
5
+ class EndpointUndefined < MapperError; end
6
+ class InvalidResponse < MapperError; end
7
+ class RecordNotFound < MapperError; end
8
+ end
9
+
10
+ end
11
+
12
+ require_relative "mapper/attributes"
13
+ require_relative "mapper/json_response"
14
+ require_relative "mapper/rest_configuration"
15
+ require_relative "mapper/rest_query"
16
+
@@ -0,0 +1,54 @@
1
+ module WCC::Data::Mapper
2
+
3
+ module Attributes
4
+
5
+ def self.included(receiver)
6
+ receiver.send(:include, InstanceMethods)
7
+ receiver.extend(ClassMethods)
8
+ end
9
+
10
+ module InstanceMethods
11
+ attr_reader :attributes
12
+
13
+ def initialize(attributes={})
14
+ @attributes = attributes
15
+ end
16
+
17
+ def [](attribute)
18
+ self.class.attributes.fetch(attribute.to_s) {
19
+ raise KeyError, "The attribute #{attribute} is not defined"
20
+ }
21
+ @attributes[attribute.to_s]
22
+ end
23
+
24
+ def []=(attribute, value)
25
+ self.class.attributes.fetch(attribute.to_s) {
26
+ raise KeyError, "The attribute #{attribute} is not defined"
27
+ }
28
+ @attributes[attribute.to_s] = value
29
+ end
30
+
31
+ end
32
+
33
+ module ClassMethods
34
+
35
+ def attribute(name, options={})
36
+ attributes[name.to_s] = options.freeze
37
+ define_method(name) { self[name] }
38
+ define_method("#{name}=") { |val| self[name] = val } if options[:writer]
39
+ end
40
+
41
+ def attributes
42
+ @attributes ||= {}
43
+ end
44
+
45
+ def inherited(subclass)
46
+ super
47
+ subclass.instance_variable_set(:@attributes, attributes.dup)
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,20 @@
1
+ module WCC::Data::Mapper
2
+
3
+ module JSONResponse
4
+
5
+ def new_from_response(response)
6
+ raise RecordNotFound if response.status == 404
7
+ case response.json
8
+ when Array
9
+ response.json.collect { |obj| new(obj) }
10
+ when Hash
11
+ new(response.json)
12
+ else
13
+ raise InvalidResponse, "Cannot build object from response!"
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
@@ -0,0 +1,36 @@
1
+ module WCC::Data::Mapper
2
+
3
+ module RESTConfiguration
4
+
5
+ def set_endpoint(app_name, uri)
6
+ @endpoint_config = {
7
+ app: app_name,
8
+ uri: uri,
9
+ }
10
+ end
11
+
12
+ def endpoint_config
13
+ @endpoint_config ||= {}
14
+ end
15
+
16
+ def endpoint
17
+ @endpoint = WCC::Data::RESTEndpoint.new(
18
+ service: service_from_app_name(endpoint_config[:app]).merge(uri: endpoint_config[:uri])
19
+ )
20
+ end
21
+
22
+ def inherited(subclass)
23
+ super
24
+ subclass.instance_variable_set(:@endpoint_config, endpoint_config.dup)
25
+ end
26
+
27
+ private
28
+
29
+ def service_from_app_name(app_name)
30
+ WCC::Data.config.applications[app_name].service
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+