api_resource 0.3.14 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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