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 CHANGED
@@ -1 +1 @@
1
- 0.3.14
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.3.14"
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-09-24"
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
- # get the relevant class
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
- case hsh.values.first
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
- # gets the current hash and calls to_query on it
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
@@ -20,14 +20,6 @@ module ApiResource
20
20
  return load(contents)
21
21
  end
22
22
  end
23
-
24
- def blank?
25
- return @internal_object.blank?
26
- end
27
-
28
- def present?
29
- return @internal_object.present?
30
- end
31
23
 
32
24
  def ==(other)
33
25
  return false if self.class != other.class
@@ -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
@@ -0,0 +1,6 @@
1
+ module ApiResource
2
+ module Decorators
3
+ extend ActiveSupport::Autoload
4
+ autoload :CachingDecorator
5
+ end
6
+ end
@@ -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