api_resource 0.4.0 → 0.4.1

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 (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