morpheus 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +134 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +44 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +7 -0
- data/lib/ext/typhoeus.rb +37 -0
- data/lib/morpheus/associations/association.rb +110 -0
- data/lib/morpheus/associations/belongs_to_association.rb +45 -0
- data/lib/morpheus/associations/has_many_association.rb +70 -0
- data/lib/morpheus/associations/has_one_association.rb +46 -0
- data/lib/morpheus/base.rb +66 -0
- data/lib/morpheus/client/associations.rb +47 -0
- data/lib/morpheus/client/inflections.rb +3 -0
- data/lib/morpheus/client/log_subscriber.rb +64 -0
- data/lib/morpheus/client/railtie.rb +25 -0
- data/lib/morpheus/configuration.rb +49 -0
- data/lib/morpheus/errors.rb +32 -0
- data/lib/morpheus/filter.rb +18 -0
- data/lib/morpheus/mixins/associations.rb +55 -0
- data/lib/morpheus/mixins/attributes.rb +133 -0
- data/lib/morpheus/mixins/conversion.rb +21 -0
- data/lib/morpheus/mixins/filtering.rb +18 -0
- data/lib/morpheus/mixins/finders.rb +58 -0
- data/lib/morpheus/mixins/introspection.rb +25 -0
- data/lib/morpheus/mixins/persistence.rb +46 -0
- data/lib/morpheus/mixins/reflections.rb +24 -0
- data/lib/morpheus/mixins/request_handling.rb +34 -0
- data/lib/morpheus/mixins/response_parsing.rb +27 -0
- data/lib/morpheus/mixins/url_support.rb +36 -0
- data/lib/morpheus/mock.rb +66 -0
- data/lib/morpheus/reflection.rb +22 -0
- data/lib/morpheus/relation.rb +57 -0
- data/lib/morpheus/request.rb +41 -0
- data/lib/morpheus/request_cache.rb +18 -0
- data/lib/morpheus/request_queue.rb +44 -0
- data/lib/morpheus/response.rb +24 -0
- data/lib/morpheus/response_parser.rb +80 -0
- data/lib/morpheus/type_caster.rb +80 -0
- data/lib/morpheus/url_builder.rb +52 -0
- data/lib/morpheus.rb +64 -0
- data/morpheus.gemspec +191 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/purchase.rb +3 -0
- data/spec/dummy/app/resources/attendee.rb +2 -0
- data/spec/dummy/app/resources/author.rb +5 -0
- data/spec/dummy/app/resources/automobile.rb +6 -0
- data/spec/dummy/app/resources/book.rb +5 -0
- data/spec/dummy/app/resources/conference.rb +3 -0
- data/spec/dummy/app/resources/dog.rb +10 -0
- data/spec/dummy/app/resources/item.rb +5 -0
- data/spec/dummy/app/resources/meeting.rb +7 -0
- data/spec/dummy/app/resources/speaker.rb +3 -0
- data/spec/dummy/app/resources/state.rb +5 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +45 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +22 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +26 -0
- data/spec/dummy/config/environments/production.rb +49 -0
- data/spec/dummy/config/environments/test.rb +35 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/morpheus.rb +3 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20110605002144_create_purchases.rb +13 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/morpheus/associations/association_spec.rb +44 -0
- data/spec/morpheus/associations/belongs_to_association_spec.rb +5 -0
- data/spec/morpheus/associations/has_many_association_spec.rb +17 -0
- data/spec/morpheus/associations/has_one_association_spec.rb +5 -0
- data/spec/morpheus/base_spec.rb +126 -0
- data/spec/morpheus/client/associations_spec.rb +44 -0
- data/spec/morpheus/configuration_spec.rb +136 -0
- data/spec/morpheus/mixins/associations_spec.rb +141 -0
- data/spec/morpheus/mixins/attributes_spec.rb +99 -0
- data/spec/morpheus/mixins/conversion_spec.rb +76 -0
- data/spec/morpheus/mixins/finders_spec.rb +255 -0
- data/spec/morpheus/mixins/introspection_spec.rb +154 -0
- data/spec/morpheus/mixins/persistence_spec.rb +161 -0
- data/spec/morpheus/mixins/reflection_spec.rb +100 -0
- data/spec/morpheus/mixins/response_parsing_spec.rb +5 -0
- data/spec/morpheus/mock_spec.rb +133 -0
- data/spec/morpheus/relation_spec.rb +71 -0
- data/spec/morpheus/request_cache_spec.rb +5 -0
- data/spec/morpheus/request_spec.rb +5 -0
- data/spec/morpheus/response_spec.rb +73 -0
- data/spec/morpheus/type_caster_spec.rb +343 -0
- data/spec/shared/active_model_lint_test.rb +14 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/configuration.rb +26 -0
- metadata +427 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Client
|
3
|
+
module Associations
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
def belongs_to_resource(association, options = {})
|
9
|
+
define_method association do
|
10
|
+
instance_variable_get("@#{association}") ||
|
11
|
+
instance_variable_set("@#{association}", Morpheus::Associations::BelongsToAssociation.new(self, association, :options => options))
|
12
|
+
end
|
13
|
+
|
14
|
+
define_method "#{association}=" do |target|
|
15
|
+
instance_variable_set("@#{association}", Morpheus::Associations::BelongsToAssociation.new(self, association, :target => target, :options => options))
|
16
|
+
send("#{association}_id=", target.id)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_many_resources(association, options = {})
|
22
|
+
define_method association do
|
23
|
+
instance_variable_get("@#{association}") ||
|
24
|
+
instance_variable_set("@#{association}", Morpheus::Associations::HasManyAssociation.new(self, association, :options => options))
|
25
|
+
end
|
26
|
+
|
27
|
+
define_method "#{association}=" do |target|
|
28
|
+
instance_variable_set("@#{association}", Morpheus::Associations::HasManyAssociation.new(self, association, :target => target, :options => options))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_one_resource(association, options = {})
|
33
|
+
define_method association do
|
34
|
+
instance_variable_get("@#{association}") ||
|
35
|
+
instance_variable_set("@#{association}", Morpheus::Associations::HasOneAssociation.new(self, association, :options => options))
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method "#{association}=" do |target|
|
39
|
+
instance_variable_set("@#{association}", Morpheus::Associations::HasOneAssociation.new(self, association, :target => target, :options => options))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Client
|
3
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@odd_or_even = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def request(event)
|
11
|
+
debug " #{event.payload[:response].cached? ? cached_request(event) : uncached_request(event)}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def uncached_request(event)
|
15
|
+
duration = event.payload[:response].time * 1000
|
16
|
+
request_statement = "#{event.payload[:class].to_s} #{event.payload[:method].to_s.upcase} (#{duration}ms)"
|
17
|
+
if odd?
|
18
|
+
request_statement = color(request_statement, MAGENTA, true)
|
19
|
+
payload = color(payload(event), nil, true)
|
20
|
+
else
|
21
|
+
request_statement = color(request_statement, CYAN, true)
|
22
|
+
payload = payload(event)
|
23
|
+
end
|
24
|
+
|
25
|
+
response_color = case event.payload[:response].code
|
26
|
+
when 200..299 then GREEN
|
27
|
+
when 400..599 then RED
|
28
|
+
else YELLOW
|
29
|
+
end
|
30
|
+
response_statement = color("[#{event.payload[:response].code}]", response_color, true)
|
31
|
+
|
32
|
+
"#{request_statement} #{response_statement} #{payload}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def cached_request(event)
|
36
|
+
duration = event.duration
|
37
|
+
request_statement = "CACHE (#{duration}ms)"
|
38
|
+
if odd?
|
39
|
+
request_statement = color(request_statement, MAGENTA, true)
|
40
|
+
payload = color(payload(event), nil, true)
|
41
|
+
else
|
42
|
+
request_statement = color(request_statement, CYAN, true)
|
43
|
+
payload = payload(event)
|
44
|
+
end
|
45
|
+
"#{request_statement} #{payload}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def payload(event)
|
49
|
+
payload = "#{event.payload[:url]}"
|
50
|
+
payload << " #{event.payload[:params].inspect}" unless event.payload[:params].blank?
|
51
|
+
payload
|
52
|
+
end
|
53
|
+
|
54
|
+
def logger
|
55
|
+
Morpheus::Configuration.logger
|
56
|
+
end
|
57
|
+
|
58
|
+
def odd?
|
59
|
+
@odd_or_even = !@odd_or_even
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Client
|
3
|
+
class Railtie < Rails::Railtie
|
4
|
+
|
5
|
+
if defined?(ActiveRecord)
|
6
|
+
ActiveRecord::Base.send(:include, Morpheus::Client::Associations)
|
7
|
+
ActiveRecord::Base.send(:include, Morpheus::UrlSupport)
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer "morpheus.client.configure_request_cache" do |app|
|
11
|
+
app.middleware.use Morpheus::RequestQueue
|
12
|
+
app.middleware.use Morpheus::RequestCache
|
13
|
+
end
|
14
|
+
|
15
|
+
initializer "morpheus.configure_logger", :after => :initialize_logger do |app|
|
16
|
+
Morpheus::Configuration.logger = Rails.logger
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer "morpheus.client.log_subscriber", :after => "morpheus.configure_logger" do
|
20
|
+
Morpheus::Client::LogSubscriber.attach_to :morpheus
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Morpheus
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
attr_accessor :username, :password
|
7
|
+
|
8
|
+
# Sets and gets the remote host service domain.
|
9
|
+
# The domain must be set before any requests can be made.
|
10
|
+
attr_writer :host
|
11
|
+
def host
|
12
|
+
@host || raise(Morpheus::ConfigurationError, "The request HOST has not been set. Please set the host using Morpheus::Configuration.host = 'http://www.example.com'")
|
13
|
+
end
|
14
|
+
|
15
|
+
# Links to the underlying libcurl request pool.
|
16
|
+
# Allows for concurrent requests.
|
17
|
+
def hydra
|
18
|
+
Typhoeus::Hydra.hydra
|
19
|
+
end
|
20
|
+
|
21
|
+
def hydra=(hydra)
|
22
|
+
Typhoeus::Hydra.hydra = hydra
|
23
|
+
end
|
24
|
+
|
25
|
+
# Handles the default logger that is used by the LogSubscriber
|
26
|
+
def logger
|
27
|
+
@logger ||= ::Logger.new(STDOUT)
|
28
|
+
end
|
29
|
+
|
30
|
+
def logger=(logger)
|
31
|
+
@logger = logger
|
32
|
+
end
|
33
|
+
|
34
|
+
# Can be set or unset to allow for test suite mocking.
|
35
|
+
def allow_net_connect=(allowed)
|
36
|
+
Typhoeus::Hydra.allow_net_connect = allowed
|
37
|
+
end
|
38
|
+
|
39
|
+
# Allow net connections by default.
|
40
|
+
allow_net_connect = true
|
41
|
+
|
42
|
+
def allow_net_connect?
|
43
|
+
Typhoeus::Hydra.allow_net_connect?
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Morpheus
|
2
|
+
# Occurs when a configuration issue occurs, like when the remote host is not set.
|
3
|
+
class ConfigurationError < ::StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# Occurs when a request from a remote host is made while Morpheus::Configuration.allow_net_connect is set to false.
|
7
|
+
class NetConnectNotAllowedError < ::StandardError
|
8
|
+
attr_accessor :request
|
9
|
+
def initialize(_request=nil)
|
10
|
+
self.request= _request
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Occurs when the remote host is unreachable.
|
15
|
+
class RemoteHostConnectionFailure < ::StandardError
|
16
|
+
end
|
17
|
+
|
18
|
+
# Occurs when there is a server error resulting in a 500 response code
|
19
|
+
class ServerError < ::StandardError
|
20
|
+
end
|
21
|
+
|
22
|
+
# Occurs when the requested resource returns a 404 error, indicating that it could not be found.
|
23
|
+
class ResourceNotFound < ::StandardError
|
24
|
+
end
|
25
|
+
|
26
|
+
# Occurs when the typecast type is not one of the known values.
|
27
|
+
class UnrecognizedTypeCastClass < ::StandardError
|
28
|
+
end
|
29
|
+
|
30
|
+
class InvalidResponseCode < ::StandardError
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Associations
|
3
|
+
autoload :Association, 'morpheus/associations/association'
|
4
|
+
autoload :BelongsToAssociation, 'morpheus/associations/belongs_to_association'
|
5
|
+
autoload :HasManyAssociation, 'morpheus/associations/has_many_association'
|
6
|
+
autoload :HasOneAssociation, 'morpheus/associations/has_one_association'
|
7
|
+
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def belongs_to(association, options = {})
|
13
|
+
attribute_id_sym = "#{association}_id".to_sym
|
14
|
+
|
15
|
+
property attribute_id_sym
|
16
|
+
|
17
|
+
create_reflection(:belongs_to, association, options)
|
18
|
+
|
19
|
+
define_method association do
|
20
|
+
instance_variable_get("@#{association}") ||
|
21
|
+
instance_variable_set("@#{association}", BelongsToAssociation.new(self, association, :options => options))
|
22
|
+
end
|
23
|
+
|
24
|
+
define_method "#{association}=" do |target|
|
25
|
+
instance_variable_set("@#{association}", BelongsToAssociation.new(self, association, :target => target, :options => options))
|
26
|
+
send("#{association}_id=", target.id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_many(association, options = {})
|
31
|
+
create_reflection(:has_many, association, options)
|
32
|
+
|
33
|
+
define_method association do
|
34
|
+
instance_variable_get("@#{association}") ||
|
35
|
+
instance_variable_set("@#{association}", HasManyAssociation.new(self, association, :options => options))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_one(association, options = {})
|
40
|
+
create_reflection(:has_one, association, options)
|
41
|
+
|
42
|
+
define_method association do
|
43
|
+
instance_variable_get("@#{association}") ||
|
44
|
+
instance_variable_set("@#{association}", HasOneAssociation.new(self, association, :options => options))
|
45
|
+
end
|
46
|
+
|
47
|
+
define_method "#{association}=" do |target|
|
48
|
+
instance_variable_set("@#{association}", HasOneAssociation.new(self, association, :target => target, :options => options))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Attributes
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def property(name, typecast_class = nil)
|
8
|
+
typecast_attributes.store(name.to_sym, typecast_class)
|
9
|
+
default_attributes.store(name.to_sym, nil)
|
10
|
+
|
11
|
+
define_attribute_accessor(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_attributes
|
15
|
+
@attributes ||= HashWithIndifferentAccess.new(superclass.respond_to?(:default_attributes) ? superclass.default_attributes : {})
|
16
|
+
end
|
17
|
+
|
18
|
+
def typecast_attributes
|
19
|
+
@typecast_attributes ||= HashWithIndifferentAccess.new(superclass.respond_to?(:default_attributes) ? superclass.default_attributes : {})
|
20
|
+
end
|
21
|
+
|
22
|
+
def attribute_defined?(attribute)
|
23
|
+
default_attributes.keys.include?(attribute.to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
def define_attribute_accessor(method)
|
27
|
+
method = method.to_s.delete('=')
|
28
|
+
define_method("#{method}=") do |value|
|
29
|
+
send("#{method}_will_change!")
|
30
|
+
update_attribute(method, value)
|
31
|
+
end
|
32
|
+
|
33
|
+
define_method(method) do
|
34
|
+
attributes[method]
|
35
|
+
end
|
36
|
+
|
37
|
+
define_attribute_methods [method]
|
38
|
+
end
|
39
|
+
private :define_attribute_accessor
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
module InstanceMethods
|
44
|
+
|
45
|
+
def attributes
|
46
|
+
@attributes ||= self.class.default_attributes.dup
|
47
|
+
end
|
48
|
+
|
49
|
+
def read_attribute_for_validation(key)
|
50
|
+
attributes[key]
|
51
|
+
end
|
52
|
+
|
53
|
+
def typecast(attribute, value)
|
54
|
+
TypeCaster.cast(value, self.class.typecast_attributes[attribute.to_sym])
|
55
|
+
end
|
56
|
+
|
57
|
+
def attributes_without_basic_attributes
|
58
|
+
attributes_to_reject = %w( id errors valid )
|
59
|
+
self.class.reflections.keys.each do |key|
|
60
|
+
attributes_to_reject.push(key.to_s)
|
61
|
+
end
|
62
|
+
attributes.reject do |key, value|
|
63
|
+
attributes_to_reject.include?(key)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def update_attribute(attribute, value)
|
68
|
+
reflection = self.class.reflect_on_association(attribute)
|
69
|
+
if reflection
|
70
|
+
update_reflection(reflection, attribute, value)
|
71
|
+
else
|
72
|
+
attributes[attribute.to_sym] = typecast(attribute, value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def update_reflection(reflection, attribute, value)
|
77
|
+
return unless value
|
78
|
+
if reflection.macro == :has_many # need to construct each member of array one-by-one
|
79
|
+
association_object = send(attribute)
|
80
|
+
value.each do |a_value|
|
81
|
+
if a_value.instance_of? reflection.klass
|
82
|
+
target = a_value
|
83
|
+
else
|
84
|
+
target = reflection.build_association(a_value)
|
85
|
+
end
|
86
|
+
association_object << target
|
87
|
+
end
|
88
|
+
elsif reflection.macro == :belongs_to
|
89
|
+
if value.instance_of? reflection.klass
|
90
|
+
target = value
|
91
|
+
else
|
92
|
+
if reflection.options[:polymorphic]
|
93
|
+
polymorphic_class = send("#{reflection.name}_type".to_sym)
|
94
|
+
polymorphic_class = value['type'] if value.include?('type')
|
95
|
+
target = polymorphic_class.constantize.new(value)
|
96
|
+
else
|
97
|
+
target = reflection.build_association(value)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
send("#{attribute}=", target)
|
101
|
+
else
|
102
|
+
if value.instance_of? reflection.klass
|
103
|
+
target = value
|
104
|
+
else
|
105
|
+
target = reflection.build_association(value)
|
106
|
+
end
|
107
|
+
send("#{attribute}=", target)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def merge_attributes(new_attributes)
|
112
|
+
new_attributes.each do |key, value|
|
113
|
+
case key.to_sym
|
114
|
+
when :errors
|
115
|
+
value.each do |k, v|
|
116
|
+
v.each do |message|
|
117
|
+
errors.add(k, message)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
when :valid
|
121
|
+
@valid = value
|
122
|
+
else
|
123
|
+
update_attribute(key, value)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
self
|
127
|
+
end
|
128
|
+
private :merge_attributes
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Conversion
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module InstanceMethods
|
6
|
+
|
7
|
+
def to_model
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_param
|
12
|
+
id.to_s unless new_record?
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_key
|
16
|
+
attributes.keys if persisted?
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Filtering
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def filter(name, &block)
|
8
|
+
(@filters ||= []) << Filter.new(name, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_filter(name)
|
12
|
+
(@filters ||= []).find { |filter| filter.name == name }
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Finders
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def find(*args)
|
8
|
+
options = args.extract_options!
|
9
|
+
if args.length == 1
|
10
|
+
case args.first
|
11
|
+
when Integer, String
|
12
|
+
attributes = [UrlBuilder.find_one(self, args.first.to_i), nil, { :id => args.first.to_i }]
|
13
|
+
get(*attributes)
|
14
|
+
when Array
|
15
|
+
attributes = UrlBuilder.find_some(self, args.first.sort).push({ :ids => args.first })
|
16
|
+
get(*attributes)
|
17
|
+
else
|
18
|
+
raise ArgumentError, "Unrecognized argument (#{args.first.inspect})."
|
19
|
+
end
|
20
|
+
else
|
21
|
+
attributes = UrlBuilder.find_some(self, args.sort).push({ :ids => args })
|
22
|
+
get(*attributes)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def all
|
27
|
+
attributes = [UrlBuilder.find_all(self), nil, {}]
|
28
|
+
get(*attributes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def first
|
32
|
+
limit(1).first
|
33
|
+
end
|
34
|
+
|
35
|
+
def where(query_attributes)
|
36
|
+
Relation.new(self).where(query_attributes)
|
37
|
+
end
|
38
|
+
|
39
|
+
def limit(amount)
|
40
|
+
Relation.new(self).limit(amount)
|
41
|
+
end
|
42
|
+
|
43
|
+
def page(page_number)
|
44
|
+
Relation.new(self).page(page_number)
|
45
|
+
end
|
46
|
+
|
47
|
+
def results_per_page
|
48
|
+
@results_per_page || 10
|
49
|
+
end
|
50
|
+
|
51
|
+
def results_per_page=(_results_per_page)
|
52
|
+
@results_per_page = _results_per_page
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Introspection
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module InstanceMethods
|
6
|
+
|
7
|
+
def persisted?
|
8
|
+
!id.nil?
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_record?
|
12
|
+
!persisted?
|
13
|
+
end
|
14
|
+
|
15
|
+
def destroyed?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def respond_to?(method, include_private = false)
|
20
|
+
attributes.include?(method) ? true : super
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Persistence
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module InstanceMethods
|
6
|
+
|
7
|
+
def save(with_validations = true)
|
8
|
+
attributes_for_save = { self.class.attributes_root => attributes_without_basic_attributes.reject { |k,v| v.nil? } }
|
9
|
+
|
10
|
+
if with_validations
|
11
|
+
return false unless valid?
|
12
|
+
end
|
13
|
+
|
14
|
+
if new_record?
|
15
|
+
built_object = self.class.post(*UrlBuilder.save(self.class, nil, attributes_for_save))
|
16
|
+
else
|
17
|
+
built_object = self.class.put(*UrlBuilder.save(self.class, id, attributes_for_save))
|
18
|
+
end
|
19
|
+
built_object.instance_variables.each do |iv|
|
20
|
+
self.instance_variable_set(iv, built_object.instance_variable_get(iv))
|
21
|
+
end
|
22
|
+
@valid
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_attributes(new_attributes)
|
26
|
+
merge_attributes(new_attributes)
|
27
|
+
save
|
28
|
+
end
|
29
|
+
|
30
|
+
def destroy
|
31
|
+
self.class.delete(UrlBuilder.destroy(self.class, id))
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
module ClassMethods
|
37
|
+
|
38
|
+
def create(new_attributes = {})
|
39
|
+
new(new_attributes).tap { |new_instance| new_instance.save }
|
40
|
+
end
|
41
|
+
alias :create! :create
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module Reflections
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def create_reflection(macro, name, options)
|
8
|
+
Reflection.new(macro, name, options).tap do |reflection|
|
9
|
+
reflections.merge!(name => reflection)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def reflections
|
14
|
+
@reflections ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def reflect_on_association(association)
|
18
|
+
reflections[association.to_sym].is_a?(Reflection) ? reflections[association.to_sym] : nil
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module RequestHandling
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def get(path, params = nil, metadata = {}, &block)
|
8
|
+
request = Request.enqueue(:get, path, params)
|
9
|
+
request.on_complete = block if block_given?
|
10
|
+
response_from_request(request, metadata)
|
11
|
+
end
|
12
|
+
|
13
|
+
def post(path, params = nil, metadata = {}, &block)
|
14
|
+
request = Request.enqueue(:post, path, params)
|
15
|
+
request.on_complete = block if block_given?
|
16
|
+
response_from_request(request, metadata)
|
17
|
+
end
|
18
|
+
|
19
|
+
def put(path, params = nil, metadata = {}, &block)
|
20
|
+
request = Request.enqueue(:put, path, params)
|
21
|
+
request.on_complete = block if block_given?
|
22
|
+
response_from_request(request, metadata)
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete(path, params = nil, metadata = {}, &block)
|
26
|
+
request = Request.enqueue(:delete, path, params)
|
27
|
+
request.on_complete = block if block_given?
|
28
|
+
response_from_request(request, metadata)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Morpheus
|
2
|
+
module ResponseParsing
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def response_from_request(request, metadata)
|
8
|
+
ResponseParser.parse(self, request, metadata)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
|
15
|
+
def build_from_response(response)
|
16
|
+
content = Yajl::Parser.parse(response.body)['content']
|
17
|
+
if content.keys.include?('type')
|
18
|
+
content['type'].constantize.new.merge_attributes(content)
|
19
|
+
else
|
20
|
+
merge_attributes(content)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
private :build_from_response
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|