api_resource 0.3.14 → 0.4.0
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.
- data/VERSION +1 -1
- data/api_resource.gemspec +5 -2
- data/lib/api_resource/associations/generic_scope.rb +68 -0
- data/lib/api_resource/associations/resource_scope.rb +11 -15
- data/lib/api_resource/associations/scope.rb +29 -12
- data/lib/api_resource/associations/single_object_proxy.rb +0 -8
- data/lib/api_resource/associations.rb +2 -1
- data/lib/api_resource/decorators/caching_decorator.rb +20 -0
- data/lib/api_resource/decorators.rb +6 -0
- data/lib/api_resource/scopes.rb +3 -0
- data/lib/api_resource.rb +1 -4
- data/spec/lib/associations_spec.rb +119 -109
- data/spec/support/mocks/association_mocks.rb +2 -1
- data/spec/support/requests/association_requests.rb +12 -4
- data/spec/support/requests/test_resource_requests.rb +3 -3
- metadata +56 -53
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/api_resource.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "api_resource"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ethan Langevin"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-10-24"
|
13
13
|
s.description = "A replacement for ActiveResource for RESTful APIs that handles associated object and multiple data sources"
|
14
14
|
s.email = "ejl6266@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
|
|
34
34
|
"lib/api_resource/associations/association_proxy.rb",
|
35
35
|
"lib/api_resource/associations/belongs_to_remote_object_proxy.rb",
|
36
36
|
"lib/api_resource/associations/dynamic_resource_scope.rb",
|
37
|
+
"lib/api_resource/associations/generic_scope.rb",
|
37
38
|
"lib/api_resource/associations/has_many_remote_object_proxy.rb",
|
38
39
|
"lib/api_resource/associations/has_many_through_remote_object_proxy.rb",
|
39
40
|
"lib/api_resource/associations/has_one_remote_object_proxy.rb",
|
@@ -50,6 +51,8 @@ Gem::Specification.new do |s|
|
|
50
51
|
"lib/api_resource/connection.rb",
|
51
52
|
"lib/api_resource/core_extensions.rb",
|
52
53
|
"lib/api_resource/custom_methods.rb",
|
54
|
+
"lib/api_resource/decorators.rb",
|
55
|
+
"lib/api_resource/decorators/caching_decorator.rb",
|
53
56
|
"lib/api_resource/exceptions.rb",
|
54
57
|
"lib/api_resource/formats.rb",
|
55
58
|
"lib/api_resource/formats/json_format.rb",
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'api_resource/associations/resource_scope'
|
2
|
+
|
3
|
+
module ApiResource
|
4
|
+
module Associations
|
5
|
+
|
6
|
+
class GenericScope < ResourceScope
|
7
|
+
attr_reader :name
|
8
|
+
attr_reader :params
|
9
|
+
attr_reader :types
|
10
|
+
attr_reader :values
|
11
|
+
|
12
|
+
# Gets called when a scope is called. Stores everything in
|
13
|
+
# this class. Sorry, couldn't be bothered to figure out the
|
14
|
+
# class hierarchy.
|
15
|
+
#
|
16
|
+
# klass - ApiResourceBase class
|
17
|
+
# current_scope - sym for the scope
|
18
|
+
# *args - arguments being passed to the scope
|
19
|
+
def initialize(klass, current_scope, *args)
|
20
|
+
|
21
|
+
@name = current_scope # contains sym with scope ie :for_provider
|
22
|
+
@params = klass.related_objects[:scopes][current_scope].keys
|
23
|
+
@types = klass.related_objects[:scopes][current_scope].values
|
24
|
+
|
25
|
+
# Bail if we have crap
|
26
|
+
if @params == nil
|
27
|
+
raise "Scope #{@name} does not exist #{klass.name}. Scopes: #{klass.related_objects[:scopes]}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# extract parent scope stuff
|
31
|
+
opts = {}
|
32
|
+
last_arg = args[args.count - 1]
|
33
|
+
if last_arg != nil && last_arg.is_a?(Hash) && last_arg[:parent] != nil
|
34
|
+
args = args.slice(0, args.count - 1)
|
35
|
+
opts = last_arg
|
36
|
+
end
|
37
|
+
|
38
|
+
# walk through parameters and types and assign values from *args to parameters
|
39
|
+
@values = []
|
40
|
+
@params.count.times do |i|
|
41
|
+
if @types[i] == :rest
|
42
|
+
@values << args.slice(i, args.count)
|
43
|
+
else
|
44
|
+
@values << args[i]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Let the parent class do its magic.
|
49
|
+
super(klass, current_scope, opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
# get the to_query value for this resource scope
|
53
|
+
def to_hash
|
54
|
+
# debugger
|
55
|
+
if @params.count == 0
|
56
|
+
scope_arguments = true
|
57
|
+
else
|
58
|
+
scope_arguments = {}
|
59
|
+
@params.count.times do |i|
|
60
|
+
scope_arguments[@params[i]] = @values[i] if @values[i] != nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
self.parent_hash.merge({@name => scope_arguments})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'api_resource/associations/scope'
|
2
2
|
|
3
3
|
module ApiResource
|
4
|
-
|
4
|
+
|
5
5
|
module Associations
|
6
|
-
|
6
|
+
|
7
7
|
class ResourceScope < Scope
|
8
|
-
|
8
|
+
|
9
9
|
include Enumerable
|
10
10
|
|
11
11
|
def internal_object
|
@@ -13,23 +13,19 @@ module ApiResource
|
|
13
13
|
@internal_object ||= self.klass.send(:find, :all, :params => self.to_hash)
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
alias_method :all, :internal_object
|
18
|
-
|
18
|
+
|
19
19
|
def each(*args, &block)
|
20
20
|
self.internal_object.each(*args, &block)
|
21
21
|
end
|
22
|
-
|
23
|
-
#
|
22
|
+
|
23
|
+
# Used by ApiResource::Scopes to create methods with the same name
|
24
|
+
# as the scope
|
25
|
+
#
|
26
|
+
# Weird place to have a factory... could have been on Scope or a separate class...
|
24
27
|
def self.class_factory(hsh)
|
25
|
-
|
26
|
-
when TrueClass, FalseClass
|
27
|
-
ApiResource::Associations::ResourceScope
|
28
|
-
when Array
|
29
|
-
ApiResource::Associations::MultiArgumentResourceScope
|
30
|
-
else
|
31
|
-
ApiResource::Associations::DynamicResourceScope
|
32
|
-
end
|
28
|
+
return ApiResource::Associations::GenericScope
|
33
29
|
end
|
34
30
|
end
|
35
31
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ApiResource
|
2
|
-
|
2
|
+
|
3
3
|
module Associations
|
4
|
-
|
4
|
+
|
5
5
|
class Scope
|
6
6
|
|
7
7
|
attr_accessor :klass, :current_scope, :internal_object
|
@@ -33,11 +33,11 @@ module ApiResource
|
|
33
33
|
# This expression substitutes the options from opts into the default attributes of the scope, it will only copy keys that exist in the original
|
34
34
|
self.scopes[self.current_scope] = opts.inject(self.scopes[current_scope]){|accum,(k,v)| accum.key?(k.to_s) ? accum.merge(k.to_s => v) : accum}
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def ttl
|
38
38
|
@ttl || 0
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Use this method to access the internal data, this guarantees that loading only occurs once per object
|
42
42
|
def internal_object
|
43
43
|
raise "Not Implemented: This method must be implemented in a subclass"
|
@@ -58,11 +58,20 @@ module ApiResource
|
|
58
58
|
def to_hash
|
59
59
|
self.parent_hash.merge(self.scopes[self.current_scope])
|
60
60
|
end
|
61
|
-
|
62
|
-
#
|
61
|
+
|
62
|
+
# takes empty hashes and replaces them with true so that to_query doesn't strip them out
|
63
|
+
def to_query_safe_hash(hash)
|
64
|
+
hash.each_pair do |k, v|
|
65
|
+
hash[k] = to_query_safe_hash(v) if v.is_a?(Hash)
|
66
|
+
hash[k] = true if v == {}
|
67
|
+
end
|
68
|
+
return hash
|
69
|
+
end
|
70
|
+
|
71
|
+
# gets the current hash and calls to_query on it
|
63
72
|
def to_query
|
64
73
|
#We need to add the unescape because to_query breaks on nested arrays
|
65
|
-
CGI.unescape(self.to_hash.to_query)
|
74
|
+
CGI.unescape(to_query_safe_hash(self.to_hash).to_query)
|
66
75
|
end
|
67
76
|
|
68
77
|
def method_missing(method, *args, &block)
|
@@ -77,16 +86,24 @@ module ApiResource
|
|
77
86
|
def to_s
|
78
87
|
self.internal_object.to_s
|
79
88
|
end
|
80
|
-
|
89
|
+
|
81
90
|
def inspect
|
82
91
|
self.internal_object.inspect
|
83
92
|
end
|
84
|
-
|
93
|
+
|
85
94
|
def blank?
|
86
95
|
self.internal_object.blank?
|
87
96
|
end
|
88
97
|
alias_method :empty?, :blank?
|
89
98
|
|
99
|
+
def present?
|
100
|
+
self.internal_object.present?
|
101
|
+
end
|
102
|
+
|
103
|
+
def expires_in(ttl)
|
104
|
+
ApiResource::Decorators::CachingDecorator.new(self, ttl)
|
105
|
+
end
|
106
|
+
|
90
107
|
protected
|
91
108
|
# scope from the parent
|
92
109
|
def parent_scope
|
@@ -109,7 +126,7 @@ module ApiResource
|
|
109
126
|
raise ArgumentError, "Unknown scope #{scp}" unless self.scope?(scp.to_s)
|
110
127
|
end
|
111
128
|
end
|
112
|
-
|
129
|
+
|
113
130
|
end
|
114
|
-
|
115
|
-
end
|
131
|
+
|
132
|
+
end
|
@@ -4,6 +4,7 @@ require 'api_resource/association_activation'
|
|
4
4
|
require 'api_resource/associations/relation_scope'
|
5
5
|
require 'api_resource/associations/resource_scope'
|
6
6
|
require 'api_resource/associations/dynamic_resource_scope'
|
7
|
+
require 'api_resource/associations/generic_scope'
|
7
8
|
require 'api_resource/associations/multi_argument_resource_scope'
|
8
9
|
require 'api_resource/associations/multi_object_proxy'
|
9
10
|
require 'api_resource/associations/single_object_proxy'
|
@@ -214,4 +215,4 @@ module ApiResource
|
|
214
215
|
|
215
216
|
end
|
216
217
|
|
217
|
-
end
|
218
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ApiResource
|
2
|
+
module Decorators
|
3
|
+
class CachingDecorator
|
4
|
+
attr_reader :owner
|
5
|
+
attr_reader :ttl
|
6
|
+
|
7
|
+
def initialize(owner, ttl)
|
8
|
+
@owner = owner
|
9
|
+
@ttl = ttl
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def method_missing(method_name, *args, &block)
|
14
|
+
ApiResource.with_ttl(self.ttl) do
|
15
|
+
self.owner.send(method_name, *args, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/api_resource/scopes.rb
CHANGED
@@ -8,6 +8,9 @@ module ApiResource
|
|
8
8
|
return self.related_objects[:scopes]
|
9
9
|
end
|
10
10
|
|
11
|
+
# Called by base.rb
|
12
|
+
# @param name is the name of the scope from the json
|
13
|
+
# @param hsh is always a hash with the arguments for the scope
|
11
14
|
def scope(name, hsh)
|
12
15
|
raise ArgumentError, "Expecting an attributes hash given #{hsh.inspect}" unless hsh.is_a?(Hash)
|
13
16
|
self.related_objects[:scopes][name.to_sym] = hsh
|
data/lib/api_resource.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
require 'active_support/inflector'
|
3
3
|
require 'active_support/core_ext'
|
4
|
-
#require 'active_support/core_ext/hash'
|
5
|
-
#require 'active_support/core_ext/object'
|
6
|
-
#require 'active_support/core_ext/class/attribute_accessors'
|
7
|
-
#require 'active_support/core_ext/class/inheritable_attributes'
|
8
4
|
require 'api_resource/core_extensions'
|
9
5
|
|
10
6
|
require 'active_model'
|
@@ -28,6 +24,7 @@ module ApiResource
|
|
28
24
|
autoload :Callbacks
|
29
25
|
autoload :Connection
|
30
26
|
autoload :CustomMethods
|
27
|
+
autoload :Decorators
|
31
28
|
autoload :Formats
|
32
29
|
autoload :Local
|
33
30
|
autoload :LogSubscriber
|