conduit 0.2.6
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/Rakefile +22 -0
- data/app/controllers/conduit/responses_controller.rb +14 -0
- data/app/models/conduit/concerns/storage.rb +41 -0
- data/app/models/conduit/request.rb +109 -0
- data/app/models/conduit/response.rb +50 -0
- data/app/models/conduit/subscription.rb +6 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20131209223122_create_conduit_requests.rb +13 -0
- data/db/migrate/20131209223734_create_conduit_responses.rb +10 -0
- data/db/migrate/20140305202729_create_conduit_subscriptions.rb +12 -0
- data/lib/conduit.rb +60 -0
- data/lib/conduit/acts_as_conduit_subscriber.rb +41 -0
- data/lib/conduit/configuration.rb +43 -0
- data/lib/conduit/core/action.rb +136 -0
- data/lib/conduit/core/connection.rb +85 -0
- data/lib/conduit/core/driver.rb +83 -0
- data/lib/conduit/core/parser.rb +55 -0
- data/lib/conduit/core/render.rb +69 -0
- data/lib/conduit/drivers/keep +0 -0
- data/lib/conduit/engine.rb +20 -0
- data/lib/conduit/storage.rb +37 -0
- data/lib/conduit/storage/aws.rb +94 -0
- data/lib/conduit/storage/file.rb +74 -0
- data/lib/conduit/util.rb +17 -0
- data/lib/conduit/version.rb +3 -0
- data/lib/tasks/conduit_tasks.rake +4 -0
- data/spec/classes/acts_as_conduit_subscriber_spec.rb +61 -0
- data/spec/classes/core/action_spec.rb +53 -0
- data/spec/classes/core/driver_spec.rb +24 -0
- data/spec/controllers/responses_controller_spec.rb +29 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +20 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/conduit.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/db/schema.rb +46 -0
- data/spec/dummy/lib/conduit/drivers/my_driver/actions/foo.rb +10 -0
- data/spec/dummy/lib/conduit/drivers/my_driver/driver.rb +7 -0
- data/spec/dummy/lib/conduit/drivers/my_driver/parsers/foo.rb +13 -0
- data/spec/dummy/lib/conduit/drivers/my_driver/views/foo.erb +5 -0
- data/spec/dummy/lib/conduit/drivers/my_driver/views/layout.erb +3 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +9707 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/tmp/conduit/1/my_driver/foo/1-response.xml +3 -0
- data/spec/dummy/tmp/conduit/1/my_driver/foo/request.xml +7 -0
- data/spec/models/conduit/request_spec.rb +50 -0
- data/spec/models/conduit/response_spec.rb +52 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/support/helper.rb +15 -0
- data/spec/support/xml/xml_request.xml +7 -0
- data/spec/support/xml/xml_response.xml +3 -0
- metadata +311 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
#
|
2
|
+
# The job of this class is to provide common
|
3
|
+
# functionality to a action class.
|
4
|
+
#
|
5
|
+
# e.g.
|
6
|
+
# => class MyAction < Conduit::Core::Action
|
7
|
+
# => required_attributes :foo, :bar
|
8
|
+
# => optional_attributes :baz
|
9
|
+
# => end
|
10
|
+
#
|
11
|
+
# => action = MyAction.new(foo: 'foo', bar: 'bar', baz: 'baz')
|
12
|
+
# => action.perform
|
13
|
+
#
|
14
|
+
|
15
|
+
require 'set'
|
16
|
+
|
17
|
+
module Conduit
|
18
|
+
module Core
|
19
|
+
class Action
|
20
|
+
|
21
|
+
def self.inherited(base)
|
22
|
+
base.send :include, Conduit::Core::Connection
|
23
|
+
base.send :include, Conduit::Core::Render
|
24
|
+
base.send :include, InstanceMethods
|
25
|
+
base.extend ClassMethods
|
26
|
+
|
27
|
+
# TODO: Move this to the driver scope
|
28
|
+
# which allows for setting this
|
29
|
+
# "globally" for the driver.
|
30
|
+
#
|
31
|
+
path = caller.first[/^[^:]+/]
|
32
|
+
define_method(:action_path) do
|
33
|
+
File.dirname(path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module ClassMethods
|
38
|
+
|
39
|
+
attr_accessor :_action_path
|
40
|
+
|
41
|
+
# Set required attributes
|
42
|
+
#
|
43
|
+
# e.g.
|
44
|
+
# => required_attributes :foo, :bar, :baz
|
45
|
+
#
|
46
|
+
def required_attributes(*args)
|
47
|
+
requirements.merge(args)
|
48
|
+
attributes.merge(args)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set optional attributes
|
52
|
+
#
|
53
|
+
# e.g.
|
54
|
+
# => optional_attributes :foo, :bar, :baz
|
55
|
+
#
|
56
|
+
def optional_attributes(*args)
|
57
|
+
attributes.merge(args)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Storage array for required attributes
|
61
|
+
#
|
62
|
+
def requirements
|
63
|
+
@requirements ||= Set.new
|
64
|
+
end
|
65
|
+
|
66
|
+
# Storage array for all attributes
|
67
|
+
#
|
68
|
+
def attributes
|
69
|
+
@attributes ||= Set.new
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
module InstanceMethods
|
75
|
+
|
76
|
+
delegate :requirements, :attributes, to: :class
|
77
|
+
|
78
|
+
def initialize(**options)
|
79
|
+
@options = options
|
80
|
+
validate!(options)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Object used for passing data to the view
|
84
|
+
# Only keys listed in attributes will be
|
85
|
+
# used.
|
86
|
+
#
|
87
|
+
def view_context
|
88
|
+
OpenStruct.new(@options.select do |k,v|
|
89
|
+
attributes.include?(k)
|
90
|
+
end)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Location where the view files can be found
|
94
|
+
# Default to lib/conduit/drivers/<drivername>/views
|
95
|
+
# Can be overriden per class.
|
96
|
+
#
|
97
|
+
def view_path
|
98
|
+
File.join(File.dirname(action_path), 'views')
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return the rendered view
|
102
|
+
#
|
103
|
+
def view
|
104
|
+
tpl = self.class.name.demodulize
|
105
|
+
.underscore.downcase
|
106
|
+
render(tpl)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Entry method. The class will use
|
110
|
+
# use this to trigger the request.
|
111
|
+
#
|
112
|
+
# Override to customize.
|
113
|
+
#
|
114
|
+
def perform
|
115
|
+
request(body: view, method: :post)
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
# Ensures that all required attributes are present
|
121
|
+
# If not all attributes are present, will raise
|
122
|
+
# an ArgumentError listing missing attributes
|
123
|
+
#
|
124
|
+
def validate!(options)
|
125
|
+
missing_keys = (requirements.to_a - options.keys)
|
126
|
+
if missing_keys.any?
|
127
|
+
raise ArgumentError,
|
128
|
+
"Missing keys: #{missing_keys.join(', ')}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#
|
2
|
+
# The job of this module is to handle network
|
3
|
+
# communication rom Conduit to a remote host.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'conduit/version'
|
7
|
+
require 'excon'
|
8
|
+
|
9
|
+
module Conduit
|
10
|
+
module Core
|
11
|
+
module Connection
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.extend ClassMethods
|
15
|
+
base.send :include, InstanceMethods
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
# Define a remote_url
|
21
|
+
#
|
22
|
+
# e.g.
|
23
|
+
# remote_url 'http://myapi.com/endpoint'
|
24
|
+
#
|
25
|
+
def remote_url(host=nil)
|
26
|
+
@remote_url ||= host
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
module InstanceMethods
|
32
|
+
|
33
|
+
delegate :remote_url, to: :class
|
34
|
+
|
35
|
+
# Make a request
|
36
|
+
#
|
37
|
+
# @param [Hash] params
|
38
|
+
# @option params [String] :body text to be sent over a socket
|
39
|
+
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request
|
40
|
+
# @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
|
41
|
+
# @option params [String] :path appears after 'scheme://host:port/'
|
42
|
+
# @option params [Fixnum] :port The port on which to connect, to the destination host
|
43
|
+
# @option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value'
|
44
|
+
# @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
|
45
|
+
# @option params [Proc] :response_block
|
46
|
+
#
|
47
|
+
#
|
48
|
+
def request(params, &block)
|
49
|
+
params[:headers] ||= {}
|
50
|
+
params[:headers]['User-Agent'] ||= "conduit/#{Conduit::VERSION}"
|
51
|
+
connection.request(params, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Connection that will be used
|
57
|
+
#
|
58
|
+
# @param [Hash] params
|
59
|
+
# @option params [String] :body Default text to be sent over a socket. Only used if :body absent in Connection#request params
|
60
|
+
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request. Only used if params[:headers] is not supplied to Connection#request
|
61
|
+
# @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
|
62
|
+
# @option params [String] :path Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request
|
63
|
+
# @option params [Fixnum] :port The port on which to connect, to the destination host
|
64
|
+
# @option params [Hash] :query Default query; appended to the 'scheme://host:port/path/' in the form of '?key=value'. Will only be used if params[:query] is not supplied to Connection#request
|
65
|
+
# @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
|
66
|
+
# @option params [String] :proxy Proxy server; e.g. 'http://myproxy.com:8888'
|
67
|
+
# @option params [Fixnum] :retry_limit Set how many times we'll retry a failed request. (Default 4)
|
68
|
+
# @option params [Class] :instrumentor Responds to #instrument as in ActiveSupport::Notifications
|
69
|
+
# @option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon'
|
70
|
+
#
|
71
|
+
# @return [Excon::Response]
|
72
|
+
#
|
73
|
+
# @raise [Excon::Errors::StubNotFound]
|
74
|
+
# @raise [Excon::Errors::Timeout]
|
75
|
+
# @raise [Excon::Errors::SocketError]
|
76
|
+
#
|
77
|
+
def connection(**params)
|
78
|
+
@excon ||= Excon.new(remote_url, params)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#
|
2
|
+
# The job of this class is to require all
|
3
|
+
# actions belonging to this driver
|
4
|
+
#
|
5
|
+
# e.g.
|
6
|
+
# => module Conduit::Driver
|
7
|
+
# => class MyDriver < Conduit::Core::Driver
|
8
|
+
# => required_credentials :foo, :bar, :baz
|
9
|
+
# =>
|
10
|
+
# => action :purchase
|
11
|
+
# => action :activate
|
12
|
+
# => action :suspend
|
13
|
+
# =>
|
14
|
+
# => end
|
15
|
+
# => end
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'set'
|
19
|
+
|
20
|
+
module Conduit
|
21
|
+
module Core
|
22
|
+
module Driver
|
23
|
+
|
24
|
+
def self.extended(base)
|
25
|
+
base.instance_variable_set("@_driver_path",
|
26
|
+
File.dirname(caller.first[/^[^:]+/]))
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set required credentials
|
30
|
+
#
|
31
|
+
# e.g.
|
32
|
+
# => required_credentials :foo, :bar, :baz
|
33
|
+
#
|
34
|
+
def required_credentials(*args)
|
35
|
+
credentials.merge(args)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set available actions
|
39
|
+
#
|
40
|
+
# e.g.
|
41
|
+
# => action :purchase
|
42
|
+
#
|
43
|
+
def action(action_name)
|
44
|
+
require File.join(@_driver_path, 'actions', action_name.to_s)
|
45
|
+
require File.join(@_driver_path, 'parsers', action_name.to_s)
|
46
|
+
actions << action_name
|
47
|
+
end
|
48
|
+
|
49
|
+
# Storage array for required credentials
|
50
|
+
#
|
51
|
+
# e.g.
|
52
|
+
# Conduit::Driver::Fusion.credentials
|
53
|
+
# => [:foo, :bar, :baz]
|
54
|
+
#
|
55
|
+
def credentials
|
56
|
+
@credentials ||= Set.new
|
57
|
+
end
|
58
|
+
|
59
|
+
# Storage array for required credentials
|
60
|
+
#
|
61
|
+
# e.g.
|
62
|
+
# Conduit::Driver::Fusion.actions
|
63
|
+
# => [:purchase]
|
64
|
+
#
|
65
|
+
def actions
|
66
|
+
@actions ||= Set.new
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Return the name of the driver
|
72
|
+
#
|
73
|
+
# e.g.
|
74
|
+
# Conduit::Drivers::Fusion.name
|
75
|
+
# => "fusion"
|
76
|
+
#
|
77
|
+
def driver_name
|
78
|
+
self.name.demodulize.underscore.downcase
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#
|
2
|
+
# The job of this class is parse a raw response
|
3
|
+
# and provide parsed attributes that can be
|
4
|
+
# predictably consumed.
|
5
|
+
#
|
6
|
+
module Conduit
|
7
|
+
module Core
|
8
|
+
class Parser
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# Define an attribute that will be publically exposed
|
13
|
+
# when dealing with conduit responses.
|
14
|
+
#
|
15
|
+
def attribute(attr_name, &block)
|
16
|
+
attributes << attr_name
|
17
|
+
define_method(attr_name, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Storage array for all attributes
|
21
|
+
#
|
22
|
+
def attributes
|
23
|
+
@attributes ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
delegate :attributes, to: :class
|
29
|
+
|
30
|
+
# Returns a hash representation of each attribute
|
31
|
+
# defined in a parser and its value.
|
32
|
+
#
|
33
|
+
def serializable_hash
|
34
|
+
attributes.inject({}) do |hash, attribute|
|
35
|
+
hash.tap { |h| h[attribute] = send(attribute) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Default response status.
|
40
|
+
# Should be overwritten by parser implementation.
|
41
|
+
#
|
42
|
+
def response_status
|
43
|
+
raise NoMethodError, "Please define response_status in your parser."
|
44
|
+
end
|
45
|
+
|
46
|
+
# Default response error container.
|
47
|
+
# Should be overwritten by parser implementation.
|
48
|
+
#
|
49
|
+
def response_errors
|
50
|
+
raise NoMethodError, "Please define response_errors in your parser."
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#
|
2
|
+
# The job of this module is to provide template
|
3
|
+
# rendering functionality. Included classes
|
4
|
+
# must provide a view_path, and view_context
|
5
|
+
# methods to be of any use.
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'tilt'
|
9
|
+
|
10
|
+
module Conduit
|
11
|
+
module Core
|
12
|
+
module Render
|
13
|
+
|
14
|
+
# Create instance variables, any of these can
|
15
|
+
# be overriden within the including class.
|
16
|
+
#
|
17
|
+
# view_path: Location where the view files are stored
|
18
|
+
# view_context: Object that contains the variables used in the template
|
19
|
+
#
|
20
|
+
def self.included(base)
|
21
|
+
attr_accessor :view_path, :view_context
|
22
|
+
end
|
23
|
+
|
24
|
+
# Render a template file
|
25
|
+
#
|
26
|
+
# e.g. Without layout
|
27
|
+
# => render :purchase, layout: false
|
28
|
+
#
|
29
|
+
# e.g. With layout
|
30
|
+
# => render :purchase
|
31
|
+
#
|
32
|
+
def render(file, layout: true)
|
33
|
+
raise ViewPathNotDefined, "" unless view_path
|
34
|
+
layout ? render_with_layout(file) : render_template(file)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Render a template file
|
38
|
+
#
|
39
|
+
# e.g. Without layout
|
40
|
+
# => render_template(:template)
|
41
|
+
#
|
42
|
+
# e.g. With layout
|
43
|
+
# => render_template(:layout) do
|
44
|
+
# => render_template(:template)
|
45
|
+
# => end
|
46
|
+
#
|
47
|
+
def render_template(file)
|
48
|
+
path = File.join(view_path, "#{file}.erb")
|
49
|
+
Tilt::ERBTemplate.new(path).render(view_context) do
|
50
|
+
yield if block_given?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Render the file with a layout
|
55
|
+
#
|
56
|
+
# e.g.
|
57
|
+
# => render_layout(:template)
|
58
|
+
#
|
59
|
+
def render_with_layout(file)
|
60
|
+
render_template(:layout) do
|
61
|
+
render_template(file)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class ViewPathNotDefined < StandardError; end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
File without changes
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Conduit
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace Conduit
|
4
|
+
|
5
|
+
config.generators do |g|
|
6
|
+
g.test_framework :rspec, :fixture => false
|
7
|
+
g.assets false
|
8
|
+
g.helper false
|
9
|
+
end
|
10
|
+
|
11
|
+
initializer "conduit.load_app_root", before: :load_config_initializers do |app|
|
12
|
+
Conduit.app_root = app.root
|
13
|
+
end
|
14
|
+
|
15
|
+
initializer 'conduit.load_drivers', after: :load_config_initializers do |app|
|
16
|
+
Conduit::Driver.load_drivers
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|