api_resource 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. metadata +179 -123
  2. data/.document +0 -5
  3. data/.rspec +0 -5
  4. data/.travis.yml +0 -4
  5. data/Gemfile +0 -37
  6. data/Gemfile.lock +0 -190
  7. data/Guardfile +0 -27
  8. data/Rakefile +0 -49
  9. data/VERSION +0 -1
  10. data/api_resource.gemspec +0 -180
  11. data/lib/api_resource.rb +0 -130
  12. data/lib/api_resource/association_activation.rb +0 -19
  13. data/lib/api_resource/associations.rb +0 -218
  14. data/lib/api_resource/associations/association_proxy.rb +0 -116
  15. data/lib/api_resource/associations/belongs_to_remote_object_proxy.rb +0 -16
  16. data/lib/api_resource/associations/dynamic_resource_scope.rb +0 -23
  17. data/lib/api_resource/associations/generic_scope.rb +0 -68
  18. data/lib/api_resource/associations/has_many_remote_object_proxy.rb +0 -16
  19. data/lib/api_resource/associations/has_many_through_remote_object_proxy.rb +0 -13
  20. data/lib/api_resource/associations/has_one_remote_object_proxy.rb +0 -24
  21. data/lib/api_resource/associations/multi_argument_resource_scope.rb +0 -15
  22. data/lib/api_resource/associations/multi_object_proxy.rb +0 -84
  23. data/lib/api_resource/associations/related_object_hash.rb +0 -12
  24. data/lib/api_resource/associations/relation_scope.rb +0 -25
  25. data/lib/api_resource/associations/resource_scope.rb +0 -32
  26. data/lib/api_resource/associations/scope.rb +0 -132
  27. data/lib/api_resource/associations/single_object_proxy.rb +0 -82
  28. data/lib/api_resource/attributes.rb +0 -243
  29. data/lib/api_resource/base.rb +0 -717
  30. data/lib/api_resource/callbacks.rb +0 -45
  31. data/lib/api_resource/connection.rb +0 -195
  32. data/lib/api_resource/core_extensions.rb +0 -7
  33. data/lib/api_resource/custom_methods.rb +0 -117
  34. data/lib/api_resource/decorators.rb +0 -6
  35. data/lib/api_resource/decorators/caching_decorator.rb +0 -20
  36. data/lib/api_resource/exceptions.rb +0 -99
  37. data/lib/api_resource/formats.rb +0 -22
  38. data/lib/api_resource/formats/json_format.rb +0 -25
  39. data/lib/api_resource/formats/xml_format.rb +0 -36
  40. data/lib/api_resource/local.rb +0 -12
  41. data/lib/api_resource/log_subscriber.rb +0 -15
  42. data/lib/api_resource/mocks.rb +0 -277
  43. data/lib/api_resource/model_errors.rb +0 -82
  44. data/lib/api_resource/observing.rb +0 -27
  45. data/lib/api_resource/railtie.rb +0 -24
  46. data/lib/api_resource/scopes.rb +0 -48
  47. data/nohup.out +0 -63
  48. data/spec/lib/api_resource_spec.rb +0 -43
  49. data/spec/lib/associations_spec.rb +0 -751
  50. data/spec/lib/attributes_spec.rb +0 -191
  51. data/spec/lib/base_spec.rb +0 -655
  52. data/spec/lib/callbacks_spec.rb +0 -68
  53. data/spec/lib/connection_spec.rb +0 -137
  54. data/spec/lib/local_spec.rb +0 -20
  55. data/spec/lib/mocks_spec.rb +0 -45
  56. data/spec/lib/model_errors_spec.rb +0 -29
  57. data/spec/lib/prefixes_spec.rb +0 -107
  58. data/spec/spec_helper.rb +0 -82
  59. data/spec/support/mocks/association_mocks.rb +0 -63
  60. data/spec/support/mocks/error_resource_mocks.rb +0 -21
  61. data/spec/support/mocks/prefix_model_mocks.rb +0 -5
  62. data/spec/support/mocks/test_resource_mocks.rb +0 -44
  63. data/spec/support/requests/association_requests.rb +0 -31
  64. data/spec/support/requests/error_resource_requests.rb +0 -25
  65. data/spec/support/requests/prefix_model_requests.rb +0 -7
  66. data/spec/support/requests/test_resource_requests.rb +0 -38
  67. data/spec/support/test_resource.rb +0 -72
  68. data/spec/tmp/DIR +0 -0
data/lib/api_resource.rb DELETED
@@ -1,130 +0,0 @@
1
- require 'active_support'
2
- require 'active_support/inflector'
3
- require 'active_support/core_ext'
4
- require 'api_resource/core_extensions'
5
-
6
- require 'active_model'
7
-
8
- require 'log4r'
9
- require 'log4r/outputter/consoleoutputters'
10
-
11
- require 'api_resource/exceptions'
12
-
13
- require 'differ'
14
- require 'colorize'
15
-
16
- module ApiResource
17
-
18
- extend ActiveSupport::Autoload
19
-
20
- autoload :Associations
21
- autoload :AssociationActivation
22
- autoload :Attributes
23
- autoload :Base
24
- autoload :Callbacks
25
- autoload :Connection
26
- autoload :CustomMethods
27
- autoload :Decorators
28
- autoload :Formats
29
- autoload :Local
30
- autoload :LogSubscriber
31
- autoload :Mocks
32
- autoload :ModelErrors
33
- autoload :Observing
34
- autoload :Scopes
35
- autoload :Validations
36
-
37
- require 'api_resource/railtie'
38
-
39
- mattr_writer :logger
40
- mattr_accessor :raise_missing_definition_error; self.raise_missing_definition_error = false
41
-
42
- DEFAULT_TIMEOUT = 10 # seconds
43
-
44
- # Load a fix for inflections for words ending in ess
45
- ActiveSupport::Inflector.inflections do |inflect|
46
- inflect.singular(/ess$/i, 'ess')
47
- end
48
-
49
- def self.load_mocks_and_factories
50
- require 'hash_dealer'
51
- Mocks.clear_endpoints
52
- Mocks.init
53
-
54
- Dir["#{File.dirname(__FILE__)}/../spec/support/requests/*.rb"].each {|f|
55
- require f
56
- }
57
- Dir["#{File.dirname(__FILE__)}/../spec/support/**/*.rb"].each {|f|
58
- require f
59
- }
60
- end
61
-
62
- class << self
63
-
64
- delegate :site, :site=, :format, :format=,
65
- :token, :token=, :timeout,
66
- :open_timeout,
67
- :reset_connection, :ttl, :ttl=,
68
- :to => ApiResource::Base
69
-
70
- end
71
-
72
- def self.cache(reset = false)
73
- @cache = nil if reset
74
- @cache ||= begin
75
- defined?(Rails) ? Rails.cache : ActiveSupport::Cache::MemoryStore.new
76
- end
77
- end
78
-
79
- # set the timeout val and reset the connection
80
- def self.timeout=(val)
81
- ApiResource::Base.timeout = val
82
- self.reset_connection
83
- val
84
- end
85
-
86
- # set the timeout val and reset the connection
87
- def self.open_timeout=(val)
88
- ApiResource::Base.open_timeout = val
89
- self.reset_connection
90
- val
91
- end
92
- self.timeout = self.open_timeout = DEFAULT_TIMEOUT
93
-
94
- # Run a block with a given token - useful for AroundFilters
95
- def self.with_token(new_token, &block)
96
- old_token = self.token
97
- begin
98
- self.token = new_token
99
- yield
100
- ensure
101
- self.token = old_token
102
- end
103
- end
104
-
105
- def self.with_ttl(new_ttl, &block)
106
- old_ttl = self.ttl
107
- begin
108
- self.ttl = new_ttl
109
- yield
110
- ensure
111
- self.ttl = old_ttl
112
- end
113
- end
114
-
115
- # logger
116
- def self.logger
117
- return @logger if @logger
118
- @logger = Log4r::Logger.new("api_resource")
119
- @logger.outputters = [Log4r::StdoutOutputter.new('console')]
120
- @logger.level = Log4r::INFO
121
- @logger
122
- end
123
-
124
- # Use this method to enable logging in the future
125
- # def self.logging(val = nil)
126
- # return (@@logging || false) unless val
127
- # return @@logging = val
128
- # end
129
-
130
- end
@@ -1,19 +0,0 @@
1
- module ApiResource
2
- module AssociationActivation
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- class_attribute :association_types
7
- # our default association types
8
- self.association_types = {:belongs_to => :single, :has_one => :single, :has_many => :multi}
9
- end
10
-
11
- module ClassMethods
12
- def activate_associations(assoc_types = nil)
13
- self.association_types = assoc_types unless assoc_types.nil?
14
- self.send(:include, ApiResource::Associations)
15
- end
16
- end
17
-
18
- end
19
- end
@@ -1,218 +0,0 @@
1
- require 'active_support'
2
- require 'active_support/string_inquirer'
3
- require 'api_resource/association_activation'
4
- require 'api_resource/associations/relation_scope'
5
- require 'api_resource/associations/resource_scope'
6
- require 'api_resource/associations/dynamic_resource_scope'
7
- require 'api_resource/associations/generic_scope'
8
- require 'api_resource/associations/multi_argument_resource_scope'
9
- require 'api_resource/associations/multi_object_proxy'
10
- require 'api_resource/associations/single_object_proxy'
11
- require 'api_resource/associations/belongs_to_remote_object_proxy'
12
- require 'api_resource/associations/has_one_remote_object_proxy'
13
- require 'api_resource/associations/has_many_remote_object_proxy'
14
- require 'api_resource/associations/has_many_through_remote_object_proxy'
15
- require 'api_resource/associations/related_object_hash'
16
-
17
- module ApiResource
18
-
19
- module Associations
20
- extend ActiveSupport::Concern
21
-
22
- included do
23
-
24
- unless self.ancestors.include?(ApiResource::AssociationActivation)
25
- raise Exception.new(
26
- "Can't include Associations without AssociationActivation"
27
- )
28
- end
29
-
30
- class_attribute :related_objects
31
- attr_accessor :assoc_attributes
32
-
33
- define_method(:assoc_attributes) do
34
- @assoc_attributes ||= Hash.new
35
- end
36
-
37
- self.clear_related_objects
38
-
39
- # we need to add an inherited method here, but it can't happen
40
- # until after this module in included/extended, so it's in its own
41
- # little mini module
42
- extend InheritedMethod
43
-
44
- self.define_association_methods
45
-
46
- end
47
-
48
- # module methods to include the proper associations in various libraries - this is usually loaded in Railties
49
- def self.activate_active_record
50
- ActiveRecord::Base.class_eval do
51
- include ApiResource::AssociationActivation
52
- self.activate_associations(
53
- :has_many_remote => :has_many_remote,
54
- :belongs_to_remote => :belongs_to_remote,
55
- :has_one_remote => :has_one_remote,
56
- )
57
- end
58
- end
59
-
60
- module InheritedMethod
61
- # define a method to reset our related objects
62
- def inherited(descendant)
63
- # we only want to do this in direct descendants of ApiResoruce::Base
64
- if self == ApiResource::Base
65
- descendant.clear_related_objects
66
- else
67
- descendant.clone_related_objects
68
- end
69
- super(descendant)
70
- end
71
- end
72
-
73
- module ClassMethods
74
-
75
- # Define the methods for creating and testing for associations, unfortunately
76
- # scopes are different enough to require different methods :(
77
- def define_association_methods
78
- self.association_types.each_key do |assoc|
79
- self.instance_eval <<-EOE, __FILE__, __LINE__ + 1
80
- def #{assoc}(*args)
81
- options = args.extract_options!
82
- options = options.with_indifferent_access
83
- # Raise an error if we have multiple args and options
84
- raise "Invalid arguments to #{assoc}" unless options.blank? || args.length == 1
85
- args.each do |arg|
86
- klass_name = (options[:class_name] ? options[:class_name].to_s.classify : arg.to_s.classify)
87
- # add this to any descendants - the other methods etc are handled by inheritance
88
- ([self] + self.descendants).each do |klass|
89
- #We need to merge upon itself to generate a new object since the children all share their related objects with each other
90
- klass.related_objects = klass.related_objects.merge(:#{assoc} => klass.related_objects[:#{assoc}].merge(arg.to_sym => klass_name))
91
- end
92
- # We need to define reader and writer methods here
93
- define_association_as_attribute(:#{assoc}, arg)
94
- end
95
- end
96
-
97
- def #{assoc}?(name)
98
- return self.related_objects[:#{assoc}][name.to_s.pluralize.to_sym].present? || self.related_objects[:#{assoc}][name.to_s.singularize.to_sym].present?
99
- end
100
-
101
- def #{assoc}_class_name(name)
102
- raise "No such" + :#{assoc}.to_s + " association on #{name}" unless self.#{assoc}?(name)
103
- return self.find_namespaced_class_name(self.related_objects[:#{assoc}][name.to_sym])
104
- end
105
-
106
- EOE
107
- # For convenience we will define the methods for testing for the existence of an association
108
- # and getting the class for an association as instance methods too to avoid tons of self.class calls
109
- self.class_eval <<-EOE, __FILE__, __LINE__ + 1
110
- def #{assoc}?(name)
111
- return self.class.#{assoc}?(name)
112
- end
113
-
114
- def #{assoc}_class_name(name)
115
- return self.class.#{assoc}_class_name(name)
116
- end
117
- EOE
118
- end
119
- end
120
-
121
- def association?(assoc)
122
- self.related_objects.any? do |key, value|
123
- next if key.to_s == "scopes"
124
- value.detect { |k,v| k.to_sym == assoc.to_sym }
125
- end
126
- end
127
-
128
- def association_names
129
- # structure is {:has_many => {"myname" => "ClassName"}}
130
- self.related_objects.clone.delete_if{|k,v| k.to_s == "scopes"}.collect{|k,v| v.keys.collect(&:to_sym)}.flatten
131
- end
132
-
133
- def association_class_name(assoc)
134
- raise ArgumentError, "#{assoc} is not a valid association of #{self}" unless self.association?(assoc)
135
- result = self.related_objects.detect do |key,value|
136
- ret = value.detect{|k,v| k.to_sym == assoc.to_sym }
137
- return self.find_namespaced_class_name(ret[1]) if ret
138
- end
139
- end
140
-
141
- protected
142
-
143
- def clear_related_objects
144
- # Hash to hold onto the definitions of the related objects
145
- self.related_objects = RelatedObjectHash.new
146
- self.association_types.keys.each do |type|
147
- self.related_objects[type] = RelatedObjectHash.new({})
148
- end
149
-
150
- # TODO :Remove scopes from related_objects.
151
- self.related_objects[:scopes] = RelatedObjectHash.new({})
152
- end
153
-
154
- def clone_related_objects
155
- # Hash to hold onto the definitions of the related objects
156
- self.related_objects = self.related_objects.clone
157
- self.association_types.keys.each do |type|
158
- self.related_objects[type] = self.related_objects[type].clone
159
- end
160
- # TODO :Remove scopes from related_objects.
161
- self.related_objects[:scopes] = self.related_objects[:scopes].clone
162
- end
163
-
164
- def define_association_as_attribute(assoc_type, assoc_name)
165
- # set up dirty tracking for associations
166
-
167
- self.class_eval <<-EOE, __FILE__, __LINE__ + 1
168
- def #{assoc_name}
169
- self.assoc_attributes[:#{assoc_name}] ||= (self.attributes[:#{assoc_name}] || #{self.association_types[assoc_type.to_sym].to_s.classify}ObjectProxy.new(self.association_class_name('#{assoc_name}'), nil, self))
170
- end
171
- def #{assoc_name}=(val)
172
- # get old internal object
173
- old_internal_object = self.#{assoc_name}.internal_object
174
- self.#{assoc_name}.internal_object = val
175
- #{assoc_name}_will_change! unless self.#{assoc_name} == old_internal_object
176
- self.#{assoc_name}.internal_object
177
- end
178
- def #{assoc_name}?
179
- self.#{assoc_name}.internal_object.present?
180
- end
181
- EOE
182
- end
183
-
184
- def find_namespaced_class_name(klass)
185
- # return the name if it is itself namespaced
186
- return klass if klass =~ /::/
187
- ancestors = self.name.split("::")
188
- if ancestors.size > 1
189
- receiver = Object
190
- namespaces = ancestors[0..-2].collect do |mod|
191
- receiver = receiver.const_get(mod)
192
- end
193
- if namespace = namespaces.reverse.detect{|ns| ns.const_defined?(klass, false)}
194
- return namespace.const_get(klass).name
195
- end
196
- end
197
-
198
- return klass
199
- end
200
-
201
- end
202
-
203
- def association?(assoc)
204
- self.class.association?(assoc)
205
- end
206
-
207
- def association_class_name(assoc)
208
- self.class.association_class_name(assoc)
209
- end
210
-
211
- # list of all association names
212
- def association_names
213
- self.class.association_names
214
- end
215
-
216
- end
217
-
218
- end
@@ -1,116 +0,0 @@
1
- module ApiResource
2
-
3
- module Associations
4
-
5
- class AssociationProxy
6
-
7
-
8
- cattr_accessor :remote_path_element; self.remote_path_element = :service_uri
9
- cattr_accessor :include_class_scopes; self.include_class_scopes = true
10
-
11
- attr_accessor :owner, :loaded, :klass, :internal_object, :remote_path, :scopes, :times_loaded
12
-
13
- # TODO: added owner - moved it to the end because the tests don't use it - it's useful here though
14
- def initialize(klass_name, contents, owner = nil)
15
- raise "Cannot create an association proxy to the unknown object #{klass_name}" unless defined?(klass_name.to_s.classify)
16
- # A simple attr_accessor for testing purposes
17
- self.times_loaded = 0
18
- self.owner = owner
19
- self.klass = klass_name.to_s.classify.constantize
20
- self.load(contents)
21
- self.loaded = {}.with_indifferent_access
22
- if self.class.include_class_scopes
23
- self.scopes = self.scopes.reverse_merge(self.klass.scopes)
24
- end
25
- # Now that we have set up all the scopes with the load method we need to create methods
26
- self.scopes.each do |key, _|
27
- next if self.respond_to?(key)
28
- self.instance_eval <<-EOE, __FILE__, __LINE__ + 1
29
- def #{key}(opts = {})
30
- @#{key} ||= ApiResource::Associations::RelationScope.new(self, :#{key}, opts)
31
- end
32
- EOE
33
- end
34
- end
35
-
36
- def serializable_hash(options = {})
37
- raise "Not Implemented: This method must be implemented in a subclass"
38
- end
39
-
40
- def scopes
41
- @scopes ||= {}.with_indifferent_access
42
- end
43
-
44
- def scope?(scp)
45
- self.scopes.keys.include?(scp.to_s)
46
- end
47
-
48
- def internal_object
49
- @internal_object ||= self.load_scope_with_options(:all, {})
50
- end
51
-
52
- def blank?
53
- self.internal_object.blank?
54
- end
55
- alias_method :empty?, :blank?
56
-
57
- def method_missing(method, *args, &block)
58
- self.internal_object.send(method, *args, &block)
59
- end
60
-
61
- def reload
62
- if self
63
- remove_instance_variable(:@internal_object) if instance_variable_defined?(:@internal_object)
64
- self.load(self.load_from_remote({}))
65
- self
66
- end
67
- end
68
-
69
- def to_s
70
- self.internal_object.to_s
71
- end
72
-
73
- def inspect
74
- self.internal_object.inspect
75
- end
76
-
77
- def ==(other)
78
- raise "Not Implemented: This method must be implemented in a subclass"
79
- end
80
-
81
- protected
82
- # This method loads a particular scope with a set of options from the remote server
83
- def load_scope_with_options(scope, options)
84
- raise "Not Implemented: This method must be implemented in a subclass"
85
- end
86
- # This method is a helper to initialize for loading the data passed in to create this object
87
- def load(contents)
88
- raise "Not Implemented: This method must be implemented in a subclass"
89
- end
90
-
91
- # get the remote URI based on our config and options
92
- def build_load_path(options)
93
- path = self.remote_path
94
- # add a format if it doesn't exist and there is no query string yet
95
- path += ".#{self.klass.format.extension}" unless path =~ /\./ || path =~/\?/
96
- # add the query string, allowing for other user-provided options in the remote_path if we have options
97
- unless options.blank?
98
- path += (path =~ /\?/ ? "&" : "?") + options.to_query
99
- end
100
- path
101
- end
102
-
103
- # get data from the remote server
104
- def load_from_remote(options)
105
- self.klass.connection.get(self.build_load_path(options))
106
- end
107
- # This method create the key for the loaded hash, it ensures that a unique set of scopes
108
- # with a unique set of options is only loaded once
109
- def loaded_hash_key(scope, options)
110
- options.to_a.sort.inject(scope) {|accum,(k,v)| accum << "_#{k}_#{v}"}
111
- end
112
- end
113
-
114
- end
115
-
116
- end