morpheus 0.3.4

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 (108) hide show
  1. data/.rvmrc +1 -0
  2. data/Gemfile +21 -0
  3. data/Gemfile.lock +134 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +44 -0
  6. data/Rakefile +48 -0
  7. data/VERSION +1 -0
  8. data/autotest/discover.rb +7 -0
  9. data/lib/ext/typhoeus.rb +37 -0
  10. data/lib/morpheus/associations/association.rb +110 -0
  11. data/lib/morpheus/associations/belongs_to_association.rb +45 -0
  12. data/lib/morpheus/associations/has_many_association.rb +70 -0
  13. data/lib/morpheus/associations/has_one_association.rb +46 -0
  14. data/lib/morpheus/base.rb +66 -0
  15. data/lib/morpheus/client/associations.rb +47 -0
  16. data/lib/morpheus/client/inflections.rb +3 -0
  17. data/lib/morpheus/client/log_subscriber.rb +64 -0
  18. data/lib/morpheus/client/railtie.rb +25 -0
  19. data/lib/morpheus/configuration.rb +49 -0
  20. data/lib/morpheus/errors.rb +32 -0
  21. data/lib/morpheus/filter.rb +18 -0
  22. data/lib/morpheus/mixins/associations.rb +55 -0
  23. data/lib/morpheus/mixins/attributes.rb +133 -0
  24. data/lib/morpheus/mixins/conversion.rb +21 -0
  25. data/lib/morpheus/mixins/filtering.rb +18 -0
  26. data/lib/morpheus/mixins/finders.rb +58 -0
  27. data/lib/morpheus/mixins/introspection.rb +25 -0
  28. data/lib/morpheus/mixins/persistence.rb +46 -0
  29. data/lib/morpheus/mixins/reflections.rb +24 -0
  30. data/lib/morpheus/mixins/request_handling.rb +34 -0
  31. data/lib/morpheus/mixins/response_parsing.rb +27 -0
  32. data/lib/morpheus/mixins/url_support.rb +36 -0
  33. data/lib/morpheus/mock.rb +66 -0
  34. data/lib/morpheus/reflection.rb +22 -0
  35. data/lib/morpheus/relation.rb +57 -0
  36. data/lib/morpheus/request.rb +41 -0
  37. data/lib/morpheus/request_cache.rb +18 -0
  38. data/lib/morpheus/request_queue.rb +44 -0
  39. data/lib/morpheus/response.rb +24 -0
  40. data/lib/morpheus/response_parser.rb +80 -0
  41. data/lib/morpheus/type_caster.rb +80 -0
  42. data/lib/morpheus/url_builder.rb +52 -0
  43. data/lib/morpheus.rb +64 -0
  44. data/morpheus.gemspec +191 -0
  45. data/spec/dummy/Rakefile +7 -0
  46. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  47. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  48. data/spec/dummy/app/models/purchase.rb +3 -0
  49. data/spec/dummy/app/resources/attendee.rb +2 -0
  50. data/spec/dummy/app/resources/author.rb +5 -0
  51. data/spec/dummy/app/resources/automobile.rb +6 -0
  52. data/spec/dummy/app/resources/book.rb +5 -0
  53. data/spec/dummy/app/resources/conference.rb +3 -0
  54. data/spec/dummy/app/resources/dog.rb +10 -0
  55. data/spec/dummy/app/resources/item.rb +5 -0
  56. data/spec/dummy/app/resources/meeting.rb +7 -0
  57. data/spec/dummy/app/resources/speaker.rb +3 -0
  58. data/spec/dummy/app/resources/state.rb +5 -0
  59. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  60. data/spec/dummy/config/application.rb +45 -0
  61. data/spec/dummy/config/boot.rb +10 -0
  62. data/spec/dummy/config/database.yml +22 -0
  63. data/spec/dummy/config/environment.rb +5 -0
  64. data/spec/dummy/config/environments/development.rb +26 -0
  65. data/spec/dummy/config/environments/production.rb +49 -0
  66. data/spec/dummy/config/environments/test.rb +35 -0
  67. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  68. data/spec/dummy/config/initializers/inflections.rb +10 -0
  69. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  70. data/spec/dummy/config/initializers/morpheus.rb +3 -0
  71. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  72. data/spec/dummy/config/initializers/session_store.rb +8 -0
  73. data/spec/dummy/config/locales/en.yml +5 -0
  74. data/spec/dummy/config/routes.rb +58 -0
  75. data/spec/dummy/config.ru +4 -0
  76. data/spec/dummy/db/migrate/20110605002144_create_purchases.rb +13 -0
  77. data/spec/dummy/db/test.sqlite3 +0 -0
  78. data/spec/dummy/public/404.html +26 -0
  79. data/spec/dummy/public/422.html +26 -0
  80. data/spec/dummy/public/500.html +26 -0
  81. data/spec/dummy/public/favicon.ico +0 -0
  82. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  83. data/spec/dummy/script/rails +6 -0
  84. data/spec/morpheus/associations/association_spec.rb +44 -0
  85. data/spec/morpheus/associations/belongs_to_association_spec.rb +5 -0
  86. data/spec/morpheus/associations/has_many_association_spec.rb +17 -0
  87. data/spec/morpheus/associations/has_one_association_spec.rb +5 -0
  88. data/spec/morpheus/base_spec.rb +126 -0
  89. data/spec/morpheus/client/associations_spec.rb +44 -0
  90. data/spec/morpheus/configuration_spec.rb +136 -0
  91. data/spec/morpheus/mixins/associations_spec.rb +141 -0
  92. data/spec/morpheus/mixins/attributes_spec.rb +99 -0
  93. data/spec/morpheus/mixins/conversion_spec.rb +76 -0
  94. data/spec/morpheus/mixins/finders_spec.rb +255 -0
  95. data/spec/morpheus/mixins/introspection_spec.rb +154 -0
  96. data/spec/morpheus/mixins/persistence_spec.rb +161 -0
  97. data/spec/morpheus/mixins/reflection_spec.rb +100 -0
  98. data/spec/morpheus/mixins/response_parsing_spec.rb +5 -0
  99. data/spec/morpheus/mock_spec.rb +133 -0
  100. data/spec/morpheus/relation_spec.rb +71 -0
  101. data/spec/morpheus/request_cache_spec.rb +5 -0
  102. data/spec/morpheus/request_spec.rb +5 -0
  103. data/spec/morpheus/response_spec.rb +73 -0
  104. data/spec/morpheus/type_caster_spec.rb +343 -0
  105. data/spec/shared/active_model_lint_test.rb +14 -0
  106. data/spec/spec_helper.rb +32 -0
  107. data/spec/support/configuration.rb +26 -0
  108. metadata +427 -0
@@ -0,0 +1,36 @@
1
+ module Morpheus
2
+ module UrlSupport
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+
7
+ def url_name
8
+ @url_name ||= model_name.underscore
9
+ end
10
+ alias_method :singular_url_name, :url_name
11
+
12
+ def plural_url_name
13
+ url_name.pluralize
14
+ end
15
+
16
+ def set_base_url(url_name)
17
+ @url_name = url_name
18
+ end
19
+
20
+ def attributes_root
21
+ @attributes_root ||= model_name.underscore
22
+ end
23
+
24
+ def set_attributes_root(name)
25
+ @attributes_root = name
26
+ end
27
+
28
+ def set_base_model_name(name)
29
+ set_base_url(name)
30
+ set_attributes_root(name)
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,66 @@
1
+ # The Mock class is used to mock out single instances of a remote resource.
2
+ # It will stub any requests to /:class_name/:id.
3
+ module Morpheus
4
+ class Mock
5
+
6
+ def initialize(mock_class, id, attributes = {}, options = {})
7
+ @mock_class = mock_class.to_s
8
+ @attributes = HashWithIndifferentAccess.new(attributes).merge({ 'id' => id })
9
+ @options = options
10
+
11
+ @attributes.keys.each do |attribute|
12
+ (class << self; self; end).class_eval do
13
+ define_method attribute do
14
+ @attributes[attribute]
15
+ end
16
+
17
+ define_method "#{attribute}=" do |value|
18
+ @attributes[attribute] = value
19
+ end
20
+ end
21
+ end
22
+
23
+ stub_finder_methods!
24
+ end
25
+
26
+ def id
27
+ @attributes[:id]
28
+ end
29
+
30
+ def status
31
+ @options[:status] || 200
32
+ end
33
+
34
+ def ==(other_object)
35
+ HashWithIndifferentAccess.new(other_object.attributes) == @attributes && other_object.class.to_s == @mock_class
36
+ end
37
+
38
+ def self.mock(mock_class, id, attributes = {}, options = {})
39
+ new(mock_class, id, attributes, options)
40
+ end
41
+
42
+ def self.clear!
43
+ Configuration.hydra.clear_stubs
44
+ end
45
+
46
+ private
47
+
48
+ def stub_finder_methods!
49
+ stub_find_one!
50
+ end
51
+
52
+ def stub_find_one!
53
+ response = Morpheus::Response.new(:code => status, :headers => "", :body => stub_find_one_body, :time => 0.3)
54
+ Configuration.hydra.stub(:get, find_one_uri).and_return(response)
55
+ end
56
+
57
+ def stub_find_one_body
58
+ { :status => status, :content => @attributes.merge({ :id => 1 }) }.to_json
59
+ end
60
+
61
+ def find_one_uri
62
+ "#{Configuration.host}/#{@mock_class.underscore.pluralize}/#{id}"
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,22 @@
1
+ module Morpheus
2
+ class Reflection
3
+ attr_reader :macro, :name, :options
4
+
5
+ def initialize(macro, name, options = {})
6
+ @macro, @name, @options = macro, name, options
7
+ end
8
+
9
+ def klass
10
+ @klass ||= class_name.constantize
11
+ end
12
+
13
+ def class_name
14
+ (options[:class_name] || name).to_s.singularize.camelize
15
+ end
16
+
17
+ def build_association(*options)
18
+ klass.new(*options)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,57 @@
1
+ module Morpheus
2
+ class Relation < ActiveSupport::BasicObject
3
+
4
+ def initialize(owner)
5
+ @owner = owner
6
+ @target = nil
7
+ @params = {}
8
+ end
9
+
10
+ def where(attributes)
11
+ @params.merge!(attributes)
12
+ self
13
+ end
14
+
15
+ def limit(amount)
16
+ @params.merge!(:limit => amount)
17
+ self
18
+ end
19
+
20
+ def page(page_number)
21
+ offset = (page_number - 1) * @owner.results_per_page
22
+ @params.merge!(:limit => @owner.results_per_page, :offset => offset)
23
+ self
24
+ end
25
+
26
+ def all
27
+ loaded_target
28
+ end
29
+
30
+ def to_a
31
+ loaded_target
32
+ end
33
+
34
+ def to_json(*args)
35
+ to_a.to_json(*args)
36
+ end
37
+
38
+ private
39
+
40
+ def loaded_target
41
+ @target ||= load_target!
42
+ end
43
+
44
+ def load_target!
45
+ @owner.get(*UrlBuilder.relation(@owner, @params))
46
+ end
47
+
48
+ def method_missing(m, *args, &block)
49
+ if Array.method_defined?(m)
50
+ loaded_target.send(m, *args, &block)
51
+ else
52
+ loaded_target.send(m, *args, &block)
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,41 @@
1
+ module Morpheus
2
+ class Request < Typhoeus::Request
3
+ attr_reader :path, :params, :method
4
+
5
+ def initialize(path, options = {})
6
+ if options[:method] == :put
7
+ options[:method] = :post
8
+ options[:params].merge!(:_method => :put)
9
+ end
10
+
11
+ options[:username] = Configuration.username if Configuration.username
12
+ options[:password] = Configuration.password if Configuration.password
13
+
14
+ super(Configuration.host + path, options)
15
+ end
16
+
17
+ def cache_key
18
+ [method, url, params].hash
19
+ end
20
+
21
+ def response=(response)
22
+ RequestCache.cache[cache_key] = response
23
+ response.tag_for_caching!
24
+ super
25
+ end
26
+
27
+ def response
28
+ RequestQueue.run! if RequestQueue.has_request?(self)
29
+ RequestCache.cache[cache_key] || super
30
+ end
31
+
32
+ def self.enqueue(method, path, params)
33
+ options = { :method => method }
34
+ options.merge!(:params => params) if params
35
+ new(path, options).tap do |request|
36
+ RequestQueue.enqueue(request)
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ module Morpheus
2
+ class RequestCache
3
+
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ self.class.cache.clear
10
+ @app.call(env)
11
+ end
12
+
13
+ def self.cache
14
+ @cache ||= {}
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,44 @@
1
+ module Morpheus
2
+ class RequestQueue
3
+
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ self.class.queue.clear
10
+ @app.call(env)
11
+ end
12
+
13
+ class << self
14
+
15
+ def enqueue(request)
16
+ queue << request
17
+ end
18
+
19
+ def queue
20
+ @queue ||= []
21
+ end
22
+
23
+ def uncached_queue
24
+ queue.collect { |request| request if RequestCache.cache[request.cache_key].nil? }.compact
25
+ end
26
+
27
+ def hydra
28
+ Configuration.hydra
29
+ end
30
+
31
+ def run!
32
+ uncached_queue.each { |request| hydra.queue(request) }
33
+ hydra.run
34
+ queue.clear
35
+ end
36
+
37
+ def has_request?(request)
38
+ queue.include?(request)
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,24 @@
1
+ module Morpheus
2
+ class Response < Typhoeus::Response
3
+
4
+ def initialize(params = {}, cached = false)
5
+ super(params)
6
+ @cached = cached
7
+ end
8
+
9
+ def cached?
10
+ !!@cached.tap do
11
+ @cached = tagged_for_caching?
12
+ end
13
+ end
14
+
15
+ def tag_for_caching!
16
+ @tagged_for_caching = true
17
+ end
18
+
19
+ def tagged_for_caching?
20
+ @tagged_for_caching ||= false
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,80 @@
1
+ module Morpheus
2
+ class ResponseParser
3
+
4
+ def initialize(owner, request, metadata)
5
+ @requesting_class = owner
6
+ @request = request
7
+ @response = request.response
8
+ @metadata = metadata
9
+ end
10
+
11
+ def self.parse(owner, request, metadata)
12
+ parser = new(owner, request, metadata)
13
+
14
+ ActiveSupport::Notifications.instrument('request.morpheus', :url => request.url, :params => request.params, :method => request.method, :class => owner, :response => request.response) do
15
+ @parsed_response = parser.parse
16
+ end
17
+ RequestCache.cache.clear unless request.method == :get
18
+ @parsed_response
19
+ end
20
+
21
+ def parse
22
+ case @response.code
23
+ when 200..299
24
+ build_from_response
25
+ when 404
26
+ raise_not_found
27
+ when 422
28
+ build_from_response
29
+ when 500..599
30
+ raise ServerError
31
+ when 0
32
+ raise RemoteHostConnectionFailure
33
+ else
34
+ raise InvalidResponseCode, "Response had error code: #{@response.code}"
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def content
41
+ @content ||= Yajl::Parser.parse(@response.body).try(:[], 'content')
42
+ end
43
+
44
+ def response_errors
45
+ @response_errors ||= Yajl::Parser.parse(@response.body).try(:[], 'errors')
46
+ end
47
+
48
+ def build_from_response
49
+ if content
50
+ if Array === content
51
+ content.collect do |attributes|
52
+ obj = build_object_with_attributes(attributes)
53
+ end
54
+ else
55
+ obj = build_object_with_attributes(content)
56
+ end
57
+ end
58
+ end
59
+
60
+ def build_object_with_attributes(attributes)
61
+ if attributes.keys.include?('type')
62
+ attributes['type'].constantize.new(attributes)
63
+ else
64
+ @requesting_class.new.send(:merge_attributes, attributes)
65
+ end
66
+ end
67
+
68
+ def raise_not_found
69
+ case
70
+ when @metadata[:ids]
71
+ raise ResourceNotFound, "Couldn't find all #{@requesting_class.to_s.pluralize} with IDs (#{@metadata[:ids].join(', ')})"
72
+ when @metadata[:id]
73
+ raise ResourceNotFound, "Couldn't find #{@requesting_class} with ID=#{@metadata[:id]}"
74
+ else
75
+ raise ResourceNotFound, "No resource was found at #{@request.url} #{response_errors.inspect if response_errors}"
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,80 @@
1
+ module Morpheus
2
+ class TypeCaster
3
+
4
+ class << self
5
+
6
+ def cast(value, typecast_class)
7
+ case typecast_class
8
+ when NilClass
9
+ value
10
+ when :string
11
+ parse_string(value)
12
+ when :integer
13
+ parse_integer(value)
14
+ when :datetime
15
+ parse_datetime(value)
16
+ when :date
17
+ parse_date(value)
18
+ when :time
19
+ parse_time(value)
20
+ when :boolean
21
+ parse_boolean(value)
22
+ else
23
+ raise UnrecognizedTypeCastClass, "Can't typecast to #{typecast_class}!"
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def parse_string(value)
30
+ value.to_s
31
+ end
32
+
33
+ def parse_integer(value)
34
+ if value.respond_to?(:to_i)
35
+ value.to_i
36
+ else
37
+ case value
38
+ when TrueClass then 1
39
+ when FalseClass then 0
40
+ when Date then value.to_time.utc.to_i
41
+ end
42
+ end
43
+ end
44
+
45
+ def parse_datetime(value)
46
+ if value.respond_to?(:to_datetime)
47
+ value.to_datetime
48
+ else
49
+ DateTime.parse(value) unless [TrueClass, FalseClass, NilClass, Fixnum].include?(value.class)
50
+ end
51
+ rescue ArgumentError
52
+ nil
53
+ end
54
+
55
+ def parse_date(value)
56
+ if value.respond_to?(:to_time)
57
+ parse_time(value).try(:to_date)
58
+ else
59
+ Date.parse(value) unless [TrueClass, FalseClass, NilClass, Fixnum].include?(value.class)
60
+ end
61
+ end
62
+
63
+ def parse_time(value)
64
+ if value.respond_to?(:to_time)
65
+ value.to_time
66
+ else
67
+ Time.parse(value) unless [TrueClass, FalseClass, NilClass, Fixnum].include?(value.class)
68
+ end
69
+ rescue ArgumentError
70
+ nil
71
+ end
72
+
73
+ def parse_boolean(value)
74
+ !["nil", nil, "false", false, "0", 0].include?(value)
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,52 @@
1
+ module Morpheus
2
+ class UrlBuilder
3
+
4
+ class << self
5
+
6
+ def find_one(klass, id)
7
+ "/#{klass.plural_url_name}/#{id}"
8
+ end
9
+
10
+ def find_some(klass, ids)
11
+ ["/#{klass.plural_url_name}", { :ids => ids }]
12
+ end
13
+
14
+ def find_all(klass)
15
+ "/#{klass.plural_url_name}"
16
+ end
17
+
18
+ def save(klass, id, parameters)
19
+ url_base = "/#{klass.plural_url_name}"
20
+ url_base << "/#{id}" if id
21
+ [url_base, parameters]
22
+ end
23
+
24
+ def belongs_to(klass, id)
25
+ "/#{klass.plural_url_name}/#{id}"
26
+ end
27
+
28
+ def has_one(primary_class, primary_key, foreign_class)
29
+ "/#{primary_class.plural_url_name}/#{primary_key}/#{foreign_class.singular_url_name}"
30
+ end
31
+
32
+ def has_many(primary_class, primary_key, foreign_class)
33
+ "/#{primary_class.plural_url_name}/#{primary_key}/#{foreign_class.plural_url_name}"
34
+ end
35
+
36
+ def relation(klass, parameters = {})
37
+ url_base = "/#{klass.plural_url_name}"
38
+ if parameters.empty?
39
+ url_base
40
+ else
41
+ [url_base, parameters]
42
+ end
43
+ end
44
+
45
+ def destroy(klass, id)
46
+ "/#{klass.plural_url_name}/#{id}"
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
data/lib/morpheus.rb ADDED
@@ -0,0 +1,64 @@
1
+ # We are using Yalj::Ruby for its JSON parsing hotness.
2
+ require 'yajl/json_gem'
3
+
4
+ # Typhoeus is used for HTTP requests. The libcurl library allows us to make multiple requests in parallel.
5
+ require 'typhoeus'
6
+
7
+ # ActiveModel provides validations, errors, naming...
8
+ require 'active_model'
9
+
10
+ # The bulk of the library consists of the Base class and its mix-in modules, the request/response wrappers,
11
+ # mocking, configuration, errors, and classes managing associations, reflections, relations, and typecasting.
12
+ module Morpheus
13
+ autoload :Base, 'morpheus/base'
14
+ autoload :Configuration, 'morpheus/configuration'
15
+ autoload :Filter, 'morpheus/filter'
16
+ autoload :Mock, 'morpheus/mock'
17
+ autoload :Reflection, 'morpheus/reflection'
18
+ autoload :Relation, 'morpheus/relation'
19
+ autoload :Request, 'morpheus/request'
20
+ autoload :RequestQueue, 'morpheus/request_queue'
21
+ autoload :RequestCache, 'morpheus/request_cache'
22
+ autoload :Response, 'morpheus/response'
23
+ autoload :ResponseParser, 'morpheus/response_parser'
24
+ autoload :TypeCaster, 'morpheus/type_caster'
25
+ autoload :UrlBuilder, 'morpheus/url_builder'
26
+
27
+ # Mixins provide behaviors to the Base class.
28
+ autoload :Associations, 'morpheus/mixins/associations'
29
+ autoload :Attributes, 'morpheus/mixins/attributes'
30
+ autoload :Conversion, 'morpheus/mixins/conversion'
31
+ autoload :Filtering, 'morpheus/mixins/filtering'
32
+ autoload :Finders, 'morpheus/mixins/finders'
33
+ autoload :Introspection, 'morpheus/mixins/introspection'
34
+ autoload :Persistence, 'morpheus/mixins/persistence'
35
+ autoload :Reflections, 'morpheus/mixins/reflections'
36
+ autoload :RequestHandling, 'morpheus/mixins/request_handling'
37
+ autoload :ResponseParsing, 'morpheus/mixins/response_parsing'
38
+ autoload :UrlSupport, 'morpheus/mixins/url_support'
39
+
40
+ # Error classes
41
+ autoload :ConfigurationError, 'morpheus/errors'
42
+ autoload :NetConnectNotAllowedError, 'morpheus/errors'
43
+ autoload :RemoteHostConnectionFailure, 'morpheus/errors'
44
+ autoload :ResourceNotFound, 'morpheus/errors'
45
+ autoload :UnrecognizedTypeCastClass, 'morpheus/errors'
46
+ autoload :ServerError, 'morpheus/errors'
47
+ autoload :InvalidResponseCode, 'morpheus/errors'
48
+ end
49
+
50
+ # Associations for application objects, typically ActiveRecord, to resource
51
+ module Morpheus
52
+ module Client
53
+ autoload :Associations, 'morpheus/client/associations'
54
+ autoload :LogSubscriber, 'morpheus/client/log_subscriber'
55
+ end
56
+ end
57
+
58
+ # The Railtie loads the LogSubscriber for printing output to the Rails log, and
59
+ # Associations which plug-in to ActiveRecord to link associated resources.
60
+ require 'morpheus/client/railtie' if defined?(Rails)
61
+ require 'morpheus/client/inflections'
62
+
63
+ # There are some Typhoeus patches contained here.
64
+ require 'ext/typhoeus'