perry 0.5.2 → 0.5.3

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.
@@ -134,11 +134,14 @@ class Perry::Adapters::AbstractAdapter
134
134
 
135
135
  # runs the adapter in the specified type mode -- designed to work with the middleware stack
136
136
  def call(mode, options)
137
- @stack ||= self.stack_items.inject(self.method(mode)) do |below, (above_klass, above_config)|
137
+ @stack ||= self.stack_items.inject(self.method(:execute)) do |below, (above_klass, above_config)|
138
138
  above_klass.new(below, above_config)
139
139
  end
140
140
 
141
141
  options[:mode] = mode.to_sym
142
+ if options[:relation] && options[:relation].modifiers_value[:noop]
143
+ options[:noop] = options[:relation].modifiers_value[:noop]
144
+ end
142
145
  @stack.call(options)
143
146
  end
144
147
 
@@ -152,6 +155,13 @@ class Perry::Adapters::AbstractAdapter
152
155
  self.config[:processors] || []
153
156
  end
154
157
 
158
+ # Proxy method for the stack and the actual adapter action. This method passes the call on to the
159
+ # appropriate method based on options[:mode] unless the query has a :noop modifier in which case
160
+ # it returns nil.
161
+ def execute(options)
162
+ self.send(options[:mode], options) unless options[:noop]
163
+ end
164
+
155
165
  # Abstract read method -- overridden by subclasses
156
166
  def read(options)
157
167
  raise(NotImplementedError,
@@ -160,14 +170,14 @@ class Perry::Adapters::AbstractAdapter
160
170
  end
161
171
 
162
172
  # Abstract write method -- overridden by subclasses
163
- def write(object)
173
+ def write(options)
164
174
  raise(NotImplementedError,
165
175
  "You must not use the abstract adapter. Implement an adapter that extends the " +
166
176
  "Perry::Adapters::AbstractAdapter class and overrides this method.")
167
177
  end
168
178
 
169
179
  # Abstract delete method -- overridden by subclasses
170
- def delete(object)
180
+ def delete(options)
171
181
  raise(NotImplementedError,
172
182
  "You must not use the abstract adapter. Implement an adapter that extends the " +
173
183
  "Perry::Adapters::AbstractAdapter class and overrides this method.")
@@ -13,6 +13,14 @@ module Perry::Adapters
13
13
 
14
14
  attr_reader :last_response
15
15
 
16
+ def read(options)
17
+ get_http(options[:relation]).parsed.tap do |result|
18
+ unless result.is_a?(Array)
19
+ raise Perry::MalformedResponse, "Expected instance of Array got #{result.inspect}"
20
+ end
21
+ end
22
+ end
23
+
16
24
  def write(options)
17
25
  object = options[:object]
18
26
  params = build_params_from_attributes(object)
@@ -25,6 +33,10 @@ module Perry::Adapters
25
33
 
26
34
  protected
27
35
 
36
+ def get_http(relation)
37
+ http_call(relation, :get, relation.to_hash.merge(relation.modifiers_value[:query] || {}))
38
+ end
39
+
28
40
  def post_http(object, params)
29
41
  http_call(object, :post, params)
30
42
  end
@@ -39,19 +51,19 @@ module Perry::Adapters
39
51
 
40
52
  def http_call(object, method, params={})
41
53
  request_klass = case method
54
+ when :get then Net::HTTP::Get
42
55
  when :post then Net::HTTP::Post
43
56
  when :put then Net::HTTP::Put
44
57
  when :delete then Net::HTTP::Delete
45
58
  end
46
59
 
47
- req_uri = self.build_uri(object, method)
60
+ req_uri = self.build_uri(object, method, params || {})
48
61
 
49
- request = if method == :delete
62
+ if [:get, :delete].include?(method)
50
63
  request = request_klass.new([req_uri.path, req_uri.query].join('?'))
51
64
  else
52
65
  request = request_klass.new(req_uri.path)
53
- request.set_form_data(params) unless method == :delete
54
- request
66
+ request.set_form_data(params)
55
67
  end
56
68
 
57
69
  self.log(params, "#{method.to_s.upcase} #{req_uri}") do
@@ -97,18 +109,20 @@ module Perry::Adapters
97
109
  end
98
110
  end
99
111
 
100
- def build_uri(object, method)
112
+ def build_uri(object, method, params={})
101
113
  url = [self.config[:host].gsub(%r{/$}, ''), self.config[:service]]
102
- unless object.new_record?
114
+ if object.is_a?(Perry::Base) && !object.new_record?
103
115
  primary_key = self.config[:primary_key] || object.primary_key
104
116
  pk_value = object.send(primary_key) or raise KeyError
105
117
  url << pk_value
106
118
  end
119
+
107
120
  uri = URI.parse "#{url.join('/')}#{self.config[:format]}"
108
121
 
109
- # TRP: method DELETE has no POST body so we have to append any default options onto the query string
110
- if method == :delete
111
- uri.query = (self.config[:default_options] || {}).collect { |key, value| "#{key}=#{value}" }.join('&')
122
+ # TRP: method GET and DELETE have no POST body so we have to append any default options onto
123
+ # the query string
124
+ if [:get, :delete].include?(method)
125
+ uri.query = (self.config[:default_options] || {}).merge(params).to_query
112
126
  end
113
127
 
114
128
  uri
data/lib/perry/base.rb CHANGED
@@ -17,7 +17,7 @@ class Perry::Base
17
17
 
18
18
  DEFAULT_PRIMARY_KEY = :id
19
19
 
20
- attr_accessor :attributes, :new_record, :saved, :read_options, :write_options
20
+ attr_accessor :attributes, :new_record, :saved, :write_options
21
21
  alias :new_record? :new_record
22
22
  alias :saved? :saved
23
23
  alias :persisted? :saved?
@@ -120,7 +120,7 @@ class Perry::Base
120
120
  protected
121
121
 
122
122
  def fetch_records(relation)
123
- self.read_adapter.call(:read, :relation => relation).compact
123
+ self.read_adapter.call(:read, :relation => relation)
124
124
  end
125
125
 
126
126
  def read_with(adapter_type)
data/lib/perry/errors.rb CHANGED
@@ -16,6 +16,11 @@ module Perry
16
16
  class RecordNotSaved < PerryError
17
17
  end
18
18
 
19
+ # Raised when the adapter returns or detects a response that Perry does not understand
20
+ #
21
+ class MalformedResponse < PerryError
22
+ end
23
+
19
24
  # Used for all association related errors
20
25
  #
21
26
  class AssociationError < PerryError
@@ -52,7 +52,7 @@ class Perry::Middlewares::CacheRecords
52
52
  cached_values
53
53
  else
54
54
  fresh_values = @adapter.call(options)
55
- self.cache_store.write(key, fresh_values) if should_store_in_cache?(fresh_values)
55
+ self.cache_store.write(key, fresh_values) if should_store_in_cache?(fresh_values, options)
56
56
  fresh_values
57
57
  end
58
58
  end
@@ -61,7 +61,8 @@ class Perry::Middlewares::CacheRecords
61
61
  Digest::MD5.hexdigest(self.class.to_s + query_hash.to_a.sort { |a,b| a.to_s.first <=> b.to_s.first }.inspect)
62
62
  end
63
63
 
64
- def should_store_in_cache?(fresh_values)
65
- !self.record_count_threshold || fresh_values.size <= self.record_count_threshold
64
+ def should_store_in_cache?(fresh_values, options)
65
+ (!self.record_count_threshold || fresh_values.size <= self.record_count_threshold) &&
66
+ !options[:noop]
66
67
  end
67
68
  end
@@ -25,7 +25,7 @@ class Perry::Middlewares::ModelBridge
25
25
  protected
26
26
 
27
27
  def build_models_from_records(records, options)
28
- if options[:relation]
28
+ if options[:relation] && records
29
29
  records.collect do |attributes|
30
30
  options[:relation].klass.new_from_data_store(attributes)
31
31
  end
@@ -37,13 +37,17 @@ class Perry::Middlewares::ModelBridge
37
37
  def update_model_after_save(response, model)
38
38
  model.saved = response.success
39
39
  if model.saved
40
- if model.new_record? && !model.read_adapter.nil?
40
+ if model.new_record? && model.read_adapter
41
41
  key = response.model_attributes[model.primary_key]
42
42
  raise Perry::PerryError.new('primary key not included in response') if key.nil?
43
43
  model.send("#{model.primary_key}=", key)
44
44
  end
45
45
  model.new_record = false
46
- model.reload unless model.read_adapter.nil?
46
+ if model.read_adapter
47
+ # Clear out cache for this model
48
+ model.class.modifiers(:reset_cache => true, :noop => true).first
49
+ model.reload
50
+ end
47
51
  else
48
52
  add_errors_to_model(response, model, 'not saved')
49
53
  end
@@ -51,6 +55,7 @@ class Perry::Middlewares::ModelBridge
51
55
 
52
56
  def update_model_after_delete(response, model)
53
57
  if response.success
58
+ model.class.scoped.modifiers(:reset_cache => true, :noop => true).first
54
59
  model.freeze!
55
60
  else
56
61
  add_errors_to_model(response, model, 'not deleted')
@@ -25,7 +25,7 @@ class Perry::Processors::PreloadAssociations
25
25
  def call(options)
26
26
  results = @adapter.call(options)
27
27
 
28
- unless results.empty?
28
+ if results && !results.empty?
29
29
  relation = options[:relation]
30
30
  (includes = relation.to_hash[:includes] || {}).keys.each do |association_id|
31
31
  association = relation.klass.defined_associations[association_id.to_sym]
@@ -77,7 +77,7 @@ class Perry::Relation
77
77
  end
78
78
 
79
79
  def to_a
80
- @records ||= fetch_records
80
+ @records ||= fetch_records || []
81
81
  end
82
82
 
83
83
  def eager_load?
data/lib/perry/version.rb CHANGED
@@ -3,7 +3,7 @@ module Perry
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 5
6
- TINY = 2
6
+ TINY = 3
7
7
 
8
8
  def self.to_s # :nodoc:
9
9
  [MAJOR, MINOR, TINY].join('.')
data/lib/perry.rb CHANGED
@@ -3,14 +3,17 @@ require 'active_support/core_ext/array'
3
3
  require 'active_support/core_ext/class/inheritable_attributes'
4
4
  require 'active_support/core_ext/hash/deep_merge'
5
5
  require 'active_support/core_ext/hash/keys'
6
+ require 'active_support/core_ext/hash/conversions'
6
7
  begin
7
8
  require 'active_support/core_ext/duplicable' #ActiveSupport 2.3.x
8
9
  Hash.send(:include, ActiveSupport::CoreExtensions::Hash::DeepMerge) unless Hash.instance_methods.include?('deep_merge')
10
+ Hash.send(:include, ActiveSupport::CoreExtensions::Hash::Conversions) unless Hash.instance_methods.include?('to_query')
9
11
  Hash.send(:include, ActiveSupport::CoreExtensions::Hash::Keys) unless Hash.instance_methods.include?('symbolize_keys')
10
12
  rescue LoadError => exception
11
13
  require 'active_support/core_ext/object/duplicable' #ActiveSupport 3.0.x
12
14
  end
13
15
  require 'active_support/core_ext/module/delegation'
16
+ require 'active_support/core_ext/object/conversions'
14
17
 
15
18
  # TRP: Used for pretty logging
16
19
  autoload :Benchmark, 'benchmark'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perry
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 13
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 5
9
- - 2
10
- version: 0.5.2
9
+ - 3
10
+ version: 0.5.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Travis Petticrew
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-16 00:00:00 -05:00
18
+ date: 2011-05-17 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency