zendesk_api 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,7 @@ GIT
7
7
  PATH
8
8
  remote: .
9
9
  specs:
10
- zendesk_api (0.1.5)
10
+ zendesk_api (0.1.6)
11
11
  faraday (>= 0.8.0)
12
12
  faraday_middleware (>= 0.8.7)
13
13
  hashie
@@ -39,13 +39,13 @@ GEM
39
39
  rspec-core (2.11.1)
40
40
  rspec-expectations (2.11.3)
41
41
  diff-lcs (~> 1.1.3)
42
- rspec-mocks (2.11.2)
42
+ rspec-mocks (2.11.3)
43
43
  simplecov (0.6.4)
44
44
  multi_json (~> 1.0)
45
45
  simplecov-html (~> 0.5.3)
46
46
  simplecov-html (0.5.3)
47
47
  vcr (2.2.5)
48
- webmock (1.8.10)
48
+ webmock (1.8.11)
49
49
  addressable (>= 2.2.7)
50
50
  crack (>= 0.1.7)
51
51
  yard (0.8.2.1)
data/Readme.md CHANGED
@@ -178,11 +178,6 @@ ticket.comment.uploads << File.new("img.jpg")
178
178
  ticket.save
179
179
  ```
180
180
 
181
- ## TODO
182
-
183
- * Search class detection
184
- * Live Testing
185
-
186
181
  ## Note on Patches/Pull Requests
187
182
  1. Fork the project.
188
183
  2. Make your feature addition or bug fix.
@@ -1,6 +1,3 @@
1
-
2
- require 'zendesk_api/core_ext/modulize'
3
- require 'zendesk_api/core_ext/snakecase'
4
1
  require 'zendesk_api/core_ext/inflection'
5
2
  require 'zendesk_api/client'
6
3
 
@@ -56,7 +56,6 @@ module ZendeskAPI
56
56
  self.send("#{association_name}=", association) # set id/ids columns
57
57
  end
58
58
 
59
-
60
59
  if association_data[:inline] == true || inline_creation
61
60
  attributes[association_name] = (association.is_a?(Collection) ? association.map(&:to_param) : association.to_param)
62
61
  end
@@ -67,6 +66,10 @@ module ZendeskAPI
67
66
  module Read
68
67
  include Rescue
69
68
 
69
+ def self.extended(klass)
70
+ klass.send(:include, ZendeskAPI::Sideloading)
71
+ end
72
+
70
73
  # Finds a resource by an id and any options passed in.
71
74
  # A custom path to search at can be passed into opts. It defaults to the {DataResource.resource_name} of the class.
72
75
  # @param [Client] client The {Client} object to be used
@@ -77,11 +80,16 @@ module ZendeskAPI
77
80
  raise ArgumentError, "No :id given" unless options[:id] || options["id"] || ancestors.include?(SingularResource)
78
81
  association = options.delete(:association) || Association.new(:class => self)
79
82
 
83
+ includes = Array(options[:include])
84
+ options[:include] = includes.join(",") if includes.any?
85
+
80
86
  response = client.connection.get(association.generate_path(options)) do |req|
81
87
  req.params = options
82
88
  end
83
89
 
84
- new(client, response.body[singular_resource_name])
90
+ new(client, response.body[singular_resource_name]).tap do |resource|
91
+ resource.set_includes(resource, includes, response.body)
92
+ end
85
93
  end
86
94
 
87
95
  rescue_client_error :find
@@ -1,3 +1,5 @@
1
+ require 'zendesk_api/helpers'
2
+
1
3
  module ZendeskAPI
2
4
  # Represents an association between two resources
3
5
  class Association
@@ -48,8 +50,56 @@ module ZendeskAPI
48
50
  namespace.join("/")
49
51
  end
50
52
 
53
+ def side_load(resources, side_loads)
54
+ key = "#{options.name}_id"
55
+ plural_key = "#{Inflection.singular options.name.to_s}_ids"
56
+
57
+ resources.each do |resource|
58
+ if resource.key?(plural_key) # Grab associations from child_ids field on resource
59
+ ids = resource.send(plural_key)
60
+
61
+ resource.send("#{options.name}=", _side_load(resource, side_loads.select {|side_load|
62
+ ids.include?(side_load[options.include_key])
63
+ }))
64
+ elsif resource.key?(key) || options.singular
65
+ # Either grab association from child_id field on resource or parent_id on child resource
66
+ if resource.key?(key)
67
+ id = resource.send(key)
68
+ key = options.include_key
69
+ else
70
+ id = resource.id
71
+ key = "#{resource.class.singular_resource_name}_id"
72
+ end
73
+
74
+ next unless id
75
+
76
+ side_load = side_loads.detect do |side_load|
77
+ id == side_load[key]
78
+ end
79
+
80
+ resource.send("#{options.name}=", side_load) if side_load
81
+ else # Grab associations from parent_id field from multiple child resources
82
+ key = "#{resource.class.singular_resource_name}_id"
83
+
84
+ resource.send("#{options.name}=", _side_load(resource, side_loads.select {|side_load|
85
+ side_load[key] == resource.id
86
+ }))
87
+ end
88
+ end
89
+ end
90
+
51
91
  private
52
92
 
93
+ def _side_load(resource, side_loads)
94
+ side_loads.map! do |side_load|
95
+ resource.send(:wrap_resource, side_load, options[:class], options)
96
+ end
97
+
98
+ ZendeskAPI::Collection.new(resource.client, options[:class]).tap do |collection|
99
+ collection.replace(side_loads)
100
+ end
101
+ end
102
+
53
103
  def build_parent_namespace(parent_class, instance, options, original_options)
54
104
  return unless association_on_parent = parent_class.associations.detect {|a| a[:class] == @options[:class] }
55
105
  [
@@ -110,7 +160,17 @@ module ZendeskAPI
110
160
  include Rescue
111
161
 
112
162
  def associations
113
- @assocations ||= []
163
+ @associations ||= []
164
+ end
165
+
166
+ def associated_with(name)
167
+ associations.inject([]) do |associated_with, association|
168
+ if association[:include] == name.to_s
169
+ associated_with.push(Association.new(association))
170
+ end
171
+
172
+ associated_with
173
+ end
114
174
  end
115
175
 
116
176
  # Represents a parent-to-child association between resources. Options to pass in are: class, path.
@@ -118,13 +178,19 @@ module ZendeskAPI
118
178
  # @param [Hash] opts The options to pass to the method definition.
119
179
  def has(resource_name, class_level_options = {})
120
180
  klass = get_class(class_level_options.delete(:class)) || get_class(resource_name)
181
+
121
182
  class_level_association = {
122
183
  :class => klass,
123
184
  :name => resource_name,
124
185
  :inline => class_level_options.delete(:inline),
125
- :path => class_level_options.delete(:path)
186
+ :path => class_level_options.delete(:path),
187
+ :include => (class_level_options.delete(:include) || klass.resource_name).to_s,
188
+ :include_key => (class_level_options.delete(:include_key) || :id).to_s,
189
+ :singular => true
126
190
  }
191
+
127
192
  associations << class_level_association
193
+
128
194
  id_column = "#{resource_name}_id"
129
195
 
130
196
  define_method "#{resource_name}_used?" do
@@ -166,13 +232,18 @@ module ZendeskAPI
166
232
  # @param [Symbol] resource The underlying resource name
167
233
  # @param [Hash] opts The options to pass to the method definition.
168
234
  def has_many(resource_name, class_level_opts = {})
169
- klass = get_class(class_level_opts.delete(:class)) || get_class(resource_name.to_s.singular)
235
+ klass = get_class(class_level_opts.delete(:class)) || get_class(Inflection.singular(resource_name.to_s))
236
+
170
237
  class_level_association = {
171
238
  :class => klass,
172
239
  :name => resource_name,
173
240
  :inline => class_level_opts.delete(:inline),
174
- :path => class_level_opts.delete(:path)
241
+ :path => class_level_opts.delete(:path),
242
+ :include => (class_level_opts.delete(:include) || klass.resource_name).to_s,
243
+ :include_key => (class_level_opts.delete(:include_key) || :id).to_s,
244
+ :singular => false
175
245
  }
246
+
176
247
  associations << class_level_association
177
248
 
178
249
  id_column = "#{resource_name}_ids"
@@ -190,7 +261,7 @@ module ZendeskAPI
190
261
 
191
262
  # find and cache association
192
263
  instance_association = Association.new(class_level_association.merge(:parent => self))
193
- singular_resource_name = resource_name.to_s.singular
264
+ singular_resource_name = Inflection.singular(resource_name.to_s)
194
265
 
195
266
  resources = if (ids = method_missing("#{singular_resource_name}_ids")) && ids.any?
196
267
  ids.map do |id|
@@ -227,7 +298,7 @@ module ZendeskAPI
227
298
  # reopened under a different superclass, an error will be thrown
228
299
  def get_class(resource)
229
300
  return false if resource.nil?
230
- res = resource.to_s.modulize
301
+ res = ZendeskAPI::Helpers.modulize_string(resource.to_s)
231
302
 
232
303
  begin
233
304
  const_get(res)
@@ -249,7 +320,7 @@ module ZendeskAPI
249
320
  # reopened under a different superclass, an error will be thrown
250
321
  def get_class(resource)
251
322
  return false if resource.nil?
252
- res = resource.to_s.modulize.split("::")
323
+ res = ZendeskAPI::Helpers.modulize_string(resource.to_s).split("::")
253
324
 
254
325
  begin
255
326
  res[1..-1].inject(ZendeskAPI.const_get(res[0])) do |iter, k|
@@ -3,6 +3,7 @@ require 'faraday_middleware'
3
3
 
4
4
  require 'zendesk_api/version'
5
5
  require 'zendesk_api/rescue'
6
+ require 'zendesk_api/sideloading'
6
7
  require 'zendesk_api/configuration'
7
8
  require 'zendesk_api/collection'
8
9
  require 'zendesk_api/lru_cache'
@@ -13,6 +14,7 @@ require 'zendesk_api/middleware/response/callback'
13
14
  require 'zendesk_api/middleware/response/deflate'
14
15
  require 'zendesk_api/middleware/response/gzip'
15
16
  require 'zendesk_api/middleware/response/parse_iso_dates'
17
+ require 'zendesk_api/middleware/response/logger'
16
18
 
17
19
  module ZendeskAPI
18
20
  class Client
@@ -30,13 +32,7 @@ module ZendeskAPI
30
32
  method = method.to_s
31
33
  options = args.last.is_a?(Hash) ? args.pop : {}
32
34
  return instance_variable_get("@#{method}") if !options.delete(:reload) && instance_variable_defined?("@#{method}")
33
- instance_variable_set("@#{method}", ZendeskAPI::Collection.new(self, ZendeskAPI.get_class(method.singular), options))
34
- end
35
-
36
- # Plays a view playlist.
37
- # @param [String/Number] id View id or 'incoming'
38
- def play(id)
39
- ZendeskAPI::Playlist.new(self, id)
35
+ instance_variable_set("@#{method}", ZendeskAPI::Collection.new(self, ZendeskAPI.get_class(Inflection.singular(method)), options))
40
36
  end
41
37
 
42
38
  # Returns the current user (aka me)
@@ -135,7 +131,7 @@ module ZendeskAPI
135
131
  builder.use Faraday::Request::BasicAuthentication, config.username, config.password
136
132
  builder.use Faraday::Response::RaiseError
137
133
  builder.use ZendeskAPI::Middleware::Response::Callback, self
138
- builder.use Faraday::Response::Logger, config.logger if config.logger
134
+ builder.use ZendeskAPI::Middleware::Response::Logger, config.logger if config.logger
139
135
  builder.use ZendeskAPI::Middleware::Response::ParseIsoDates
140
136
  builder.response :json, :content_type => 'application/json'
141
137
  builder.use ZendeskAPI::Middleware::Response::Gzip
@@ -2,19 +2,23 @@ require 'zendesk_api/resource'
2
2
  require 'zendesk_api/resources/misc'
3
3
  require 'zendesk_api/resources/ticket'
4
4
  require 'zendesk_api/resources/user'
5
- require 'zendesk_api/resources/playlist'
6
5
 
7
6
  module ZendeskAPI
8
7
  # Represents a collection of resources. Lazily loaded, resources aren't
9
8
  # actually fetched until explicitly needed (e.g. #each, {#fetch}).
10
9
  class Collection
11
- SPECIALLY_JOINED_PARAMS = [:include, :ids, :only]
10
+ include ZendeskAPI::Sideloading
11
+
12
+ SPECIALLY_JOINED_PARAMS = [:ids, :only]
12
13
 
13
14
  include Rescue
14
15
 
15
16
  # @return [ZendeskAPI::Association] The class association
16
17
  attr_reader :association
17
18
 
19
+ # @return [Faraday::Response] The last response
20
+ attr_reader :response
21
+
18
22
  # Creates a new Collection instance. Does not fetch resources.
19
23
  # Additional options are: verb (default: GET), path (default: resource param), page, per_page.
20
24
  # @param [Client] client The {Client} to use.
@@ -41,6 +45,7 @@ module ZendeskAPI
41
45
  @collection_path ||= [@resource]
42
46
  @resource_class = resource
43
47
  @fetchable = true
48
+ @includes = Array(@options.delete(:include))
44
49
 
45
50
  # Used for Attachments, TicketComment
46
51
  if @resource_class.superclass == ZendeskAPI::Data
@@ -112,6 +117,10 @@ module ZendeskAPI
112
117
  self
113
118
  end
114
119
 
120
+ def include(*sideloads)
121
+ self.tap { @includes.concat(sideloads.map(&:to_s)) }
122
+ end
123
+
115
124
  def <<(item)
116
125
  fetch
117
126
  if item.is_a?(Resource)
@@ -134,6 +143,7 @@ module ZendeskAPI
134
143
  # @param [Boolean] reload Whether to disregard cache
135
144
  def fetch(reload = false)
136
145
  return @resources if @resources && (!@fetchable || !reload)
146
+
137
147
  if association && association.options.parent && association.options.parent.new_record?
138
148
  return @resources = []
139
149
  end
@@ -145,9 +155,11 @@ module ZendeskAPI
145
155
  path = self.path
146
156
  end
147
157
 
148
- response = @client.connection.send(@verb || "get", path) do |req|
158
+ @response = @client.connection.send(@verb || "get", path) do |req|
149
159
  opts = @options.delete_if {|k, v| v.nil?}
150
160
 
161
+ req.params.merge!(:include => @includes.join(",")) if @includes.any?
162
+
151
163
  if %w{put post}.include?(@verb.to_s)
152
164
  req.body = opts
153
165
  else
@@ -155,21 +167,29 @@ module ZendeskAPI
155
167
  end
156
168
  end
157
169
 
158
- results = response.body[@resource_class.model_key] || response.body["results"]
159
- @resources = results.map { |res| @resource_class.new(@client, res) }
170
+ body = @response.body.dup
171
+
172
+ results = body.delete(@resource_class.model_key) || body.delete("results")
173
+ @resources = results.map {|res| @resource_class.new(@client, res)}
160
174
 
161
- @count = (response.body["count"] || @resources.size).to_i
162
- @next_page, @prev_page = response.body["next_page"], response.body["previous_page"]
175
+ set_page_and_count(body)
176
+ set_includes(@resources, @includes, body)
177
+
178
+ @resources
179
+ end
180
+
181
+ def set_page_and_count(body)
182
+ @count = (body["count"] || @resources.size).to_i
183
+ @next_page, @prev_page = body["next_page"], body["previous_page"]
163
184
 
164
185
  if @next_page =~ /page=(\d+)/
165
186
  @options["page"] = $1.to_i - 1
166
187
  elsif @prev_page =~ /page=(\d+)/
167
188
  @options["page"] = $1.to_i + 1
168
189
  end
169
-
170
- @resources
171
190
  end
172
191
 
192
+
173
193
  rescue_client_error :fetch, :with => lambda { Array.new }
174
194
 
175
195
  # Alias for fetch(false)
@@ -1,13 +1,3 @@
1
1
  require 'inflection'
2
2
 
3
3
  Inflection.plural_rule 'forum', 'forums'
4
-
5
- class String
6
- def singular
7
- Inflection.singular(self)
8
- end
9
-
10
- def plural
11
- Inflection.plural(self)
12
- end
13
- end
@@ -0,0 +1,23 @@
1
+ module ZendeskAPI
2
+ module Helpers
3
+ # From https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/modulize.rb
4
+ def self.modulize_string(string)
5
+ #gsub('__','/'). # why was this ever here?
6
+ string.gsub(/__(.?)/){ "::#{$1.upcase}" }.
7
+ gsub(/\/(.?)/){ "::#{$1.upcase}" }.
8
+ gsub(/(?:_+|-+)([a-z])/){ $1.upcase }.
9
+ gsub(/(\A|\s)([a-z])/){ $1 + $2.upcase }
10
+ end
11
+
12
+ # From https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/snakecase.rb
13
+ def self.snakecase_string(string)
14
+ #gsub(/::/, '/').
15
+ string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
16
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
17
+ tr('-', '_').
18
+ gsub(/\s/, '_').
19
+ gsub(/__+/, '_').
20
+ downcase
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ require 'faraday_middleware/response_middleware'
2
+
3
+ module ZendeskAPI
4
+ module Middleware
5
+ module Response
6
+ # Faraday middleware to handle logging
7
+ class Logger < Faraday::Response::Middleware
8
+ def initialize(app, logger = nil)
9
+ super(app)
10
+ @logger = logger || begin
11
+ require 'logger'
12
+ ::Logger.new(STDOUT)
13
+ end
14
+ end
15
+
16
+ def call(env)
17
+ @logger.info "#{env[:method]} #{env[:url].to_s}"
18
+ @logger.debug dump_debug(env, :request_headers)
19
+ super
20
+ end
21
+
22
+ def on_complete(env)
23
+ @logger.info("Status #{env[:status].to_s}")
24
+ @logger.debug dump_debug(env, :response_headers)
25
+ end
26
+
27
+ private
28
+
29
+ def dump_debug(env, headers_key)
30
+ info = env[headers_key].map { |k, v| " #{k}: #{v.inspect}" }.join("\n")
31
+ unless env[:body].nil?
32
+ info.concat("\n")
33
+ info.concat(env[:body].inspect)
34
+ end
35
+ info
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,4 @@
1
+ require 'zendesk_api/helpers'
1
2
  require 'zendesk_api/trackie'
2
3
  require 'zendesk_api/actions'
3
4
  require 'zendesk_api/association'
@@ -12,12 +13,12 @@ module ZendeskAPI
12
13
  class << self
13
14
  # The singular resource name taken from the class name (e.g. ZendeskAPI::Ticket -> ticket)
14
15
  def singular_resource_name
15
- @singular_resource_name ||= to_s.split("::").last.snakecase
16
+ @singular_resource_name ||= ZendeskAPI::Helpers.snakecase_string(to_s.split("::").last)
16
17
  end
17
18
 
18
19
  # The resource name taken from the class name (e.g. ZendeskAPI::Ticket -> tickets)
19
20
  def resource_name
20
- @resource_name ||= singular_resource_name.plural
21
+ @resource_name ||= Inflection.plural(singular_resource_name)
21
22
  end
22
23
 
23
24
  alias :model_key :resource_name
@@ -71,6 +72,13 @@ module ZendeskAPI
71
72
  id.nil?
72
73
  end
73
74
 
75
+ def loaded_associations
76
+ self.class.associations.select do |association|
77
+ loaded = @attributes.method_missing(association[:name])
78
+ loaded && !(loaded.respond_to?(:empty?) && loaded.empty?)
79
+ end
80
+ end
81
+
74
82
  # Returns the path to the resource
75
83
  def path(*args)
76
84
  @association.generate_path(self, *args)
@@ -54,7 +54,20 @@ module ZendeskAPI
54
54
  class Bookmark < Resource; end
55
55
  class Macro < DataResource; end
56
56
 
57
- class Search < DataResource
57
+ module Search
58
+ class Result < Data; end
59
+
60
+ def self.new(client, attributes)
61
+ result_type = attributes["result_type"]
62
+
63
+ if result_type
64
+ result_type = ZendeskAPI::Helpers.modulize_string(result_type)
65
+ klass = ZendeskAPI.const_get(result_type) rescue nil
66
+ end
67
+
68
+ (klass || Result).new(client, attributes)
69
+ end
70
+
58
71
  def self.resource_name
59
72
  "search"
60
73
  end
@@ -14,6 +14,7 @@ module ZendeskAPI
14
14
  include Save
15
15
 
16
16
  has_many :uploads, :class => :attachment, :inline => true
17
+ has :author, :class => :user
17
18
 
18
19
  def save
19
20
  save_associations
@@ -40,6 +41,7 @@ module ZendeskAPI
40
41
  has :organization
41
42
 
42
43
  has :comment, :class => :ticket_comment, :inline => true
44
+ has :last_comment, :class => :ticket_comment, :inline => true
43
45
 
44
46
  # Gets a incremental export of tickets from the start_time until now.
45
47
  # @param [Client] client The {Client} object to be used
@@ -17,8 +17,19 @@ module ZendeskAPI
17
17
  put :request_verification
18
18
  end
19
19
 
20
+ def initialize(*)
21
+ super
22
+
23
+ # Needed for side-loading to work
24
+ self.role_id = role.id if self.key?(:role)
25
+ end
26
+
20
27
  has :organization
21
- has :custom_role
28
+
29
+ has :custom_role, :include => :roles
30
+ has :role, :inline => true, :include_key => :name
31
+ has :ability, :inline => true
32
+
22
33
  has_many :identities
23
34
 
24
35
  has_many :requests
@@ -39,7 +50,9 @@ module ZendeskAPI
39
50
  end
40
51
 
41
52
  class Organization < Resource
53
+ has :ability, :inline => true
42
54
  has :group
55
+
43
56
  has_many :tickets
44
57
  has_many :users
45
58
  end
@@ -0,0 +1,50 @@
1
+ module ZendeskAPI
2
+ module Sideloading
3
+ def self.included(klass)
4
+ klass.send(:attr_reader, :included)
5
+ end
6
+
7
+ def set_includes(resource_or_resources, includes, body)
8
+ @included = {}
9
+
10
+ includes.each do |side_load|
11
+ unless body.key?(side_load.to_s)
12
+ @client.config.logger.warn "Missing #{side_load} key in response -- cannot side load"
13
+ end
14
+ end
15
+
16
+ resources = [resource_or_resources].flatten.compact
17
+ resource_class = resources.first.class
18
+
19
+ return if resources.empty?
20
+
21
+ body.keys.each do |name|
22
+ @included[name] = body[name]
23
+ _side_load(name, resource_class, resources)
24
+ end
25
+ end
26
+
27
+
28
+ private
29
+
30
+ # Traverses the resource looking for associations
31
+ # then descends into those associations and checks for applicable
32
+ # resources to side load
33
+ def _side_load(name, klass, resources)
34
+ associations = klass.associated_with(name)
35
+
36
+ associations.each do |association|
37
+ association.side_load(resources, @included[name])
38
+ end
39
+
40
+ resources.each do |resource|
41
+ loaded_associations = resource.loaded_associations
42
+ loaded_associations.each do |association|
43
+ loaded = resource.send(association[:name])
44
+ next unless loaded
45
+ _side_load(name, association[:class], [loaded].flatten)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,3 +1,3 @@
1
1
  module ZendeskAPI
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
@@ -22,7 +22,6 @@ describe ZendeskAPI::Group, :delete_after do
22
22
  after(:each) do
23
23
  VCR.use_cassette("read_ZendeskAPI::User_groups_delete") do
24
24
  @object.destroy
25
- @membership.destroy
26
25
  end
27
26
  end
28
27
 
@@ -24,13 +24,13 @@ describe ZendeskAPI::Ticket do
24
24
  context "recent tickets" do
25
25
  before(:each) do
26
26
  VCR.use_cassette("visit_recent_ticket") do
27
- client.connection.get("/tickets/#{@object.id}") do |req|
27
+ client.connection.get("/tickets/1") do |req|
28
28
  req.headers[:Accept] = "*/*"
29
29
  end
30
30
  end
31
31
  end
32
32
 
33
- it_should_be_readable :tickets, :recent, :create => true
33
+ it_should_be_readable :tickets, :recent
34
34
  end
35
35
 
36
36
  describe ".incremental_export" do
@@ -87,7 +87,7 @@ describe ZendeskAPI::Client do
87
87
  subject { true }
88
88
 
89
89
  it "should log in faraday" do
90
- @client.connection.builder.handlers.should include(Faraday::Response::Logger)
90
+ @client.connection.builder.handlers.should include(ZendeskAPI::Middleware::Response::Logger)
91
91
  end
92
92
 
93
93
  context "with a request" do
@@ -102,7 +102,7 @@ describe ZendeskAPI::Client do
102
102
  subject { false }
103
103
 
104
104
  it "should not log" do
105
- @client.connection.builder.handlers.should_not include(Faraday::Response::Logger)
105
+ @client.connection.builder.handlers.should_not include(ZendeskAPI::Middleware::Response::Logger)
106
106
  end
107
107
  end
108
108
 
@@ -110,7 +110,7 @@ describe ZendeskAPI::Client do
110
110
  subject { nil }
111
111
 
112
112
  it "should log" do
113
- @client.connection.builder.handlers.should include(Faraday::Response::Logger)
113
+ @client.connection.builder.handlers.should include(ZendeskAPI::Middleware::Response::Logger)
114
114
  end
115
115
  end
116
116
 
@@ -119,7 +119,7 @@ describe ZendeskAPI::Client do
119
119
  subject { Logger.new(out) }
120
120
 
121
121
  it "should log" do
122
- @client.connection.builder.handlers.should include(Faraday::Response::Logger)
122
+ @client.connection.builder.handlers.should include(ZendeskAPI::Middleware::Response::Logger)
123
123
  end
124
124
 
125
125
  context "with a request" do
@@ -159,19 +159,6 @@ describe ZendeskAPI::Client do
159
159
  end
160
160
  end
161
161
 
162
- context "#play" do
163
- # TODO may be able to be replaced by VCR
164
- before(:each) do
165
- stub_request(:get, %r{play}).to_return do
166
- { :status => 302 }
167
- end
168
- end
169
-
170
- it "should return an instance of ZendeskAPI::Playlist" do
171
- subject.play(1).should be_instance_of(ZendeskAPI::Playlist)
172
- end
173
- end
174
-
175
162
  it "can be subclassed" do
176
163
  client = SimpleClient.new do |config|
177
164
  config.allow_http = true
@@ -401,6 +401,149 @@ describe ZendeskAPI::Collection do
401
401
  end
402
402
  end
403
403
 
404
+ context "side loading" do
405
+ before(:each) do
406
+ subject.include(:nil_resources)
407
+ end
408
+
409
+ context "singular id on resource" do
410
+ before(:each) do
411
+ ZendeskAPI::TestResource.has :nil_resource
412
+
413
+ stub_json_request(:get, %r{test_resources\?include=nil_resources}, json(
414
+ :test_resources => [{ :id => 1, :nil_resource_id => 4 }],
415
+ :nil_resources => [{ :id => 1, :name => :bye }, { :id => 4, :name => :hi }]
416
+ ))
417
+
418
+ subject.fetch(true)
419
+
420
+ @resource = subject.detect {|res| res.id == 1}
421
+ end
422
+
423
+ it "should side load nil_resources" do
424
+ @resource.nil_resource.should_not be_nil
425
+ end
426
+
427
+ it "should side load the correct nil_resource" do
428
+ @resource.nil_resource.name.should == "hi"
429
+ end
430
+ end
431
+
432
+ context "plural ids on resource" do
433
+ before(:each) do
434
+ ZendeskAPI::TestResource.has_many :nil_resources
435
+
436
+ stub_json_request(:get, %r{test_resources\?include=nil_resources}, json(
437
+ :test_resources => [{ :id => 1, :nil_resource_ids => [1, 4] }],
438
+ :nil_resources => [{ :id => 1, :name => :hi }, { :id => 4, :name => :hello }, { :id => 5, :name => :goodbye }]
439
+ ))
440
+
441
+ subject.fetch(true)
442
+
443
+ @resource = subject.detect {|res| res.id == 1}
444
+ end
445
+
446
+ it "should side load nil_resources" do
447
+ @resource.nil_resources.should_not be_empty
448
+ end
449
+
450
+ it "should side load the correct nil_resources" do
451
+ @resource.nil_resources.map(&:name).should == %w{hi hello}
452
+ end
453
+ end
454
+
455
+ context "ids in side load" do
456
+ before(:each) do
457
+ ZendeskAPI::TestResource.has_many :nil_resources
458
+
459
+ stub_json_request(:get, %r{test_resources\?include=nil_resources}, json(
460
+ :test_resources => [{ :id => 1 }],
461
+ :nil_resources => [{ :id => 1, :test_resource_id => 2 }, { :id => 2, :test_resource_id => 1 }, { :id => 4, :test_resource_id => 1 }]
462
+ ))
463
+
464
+ subject.fetch(true)
465
+ @resource = subject.detect {|res| res.id == 1}
466
+ end
467
+
468
+ it "should side load nil_resources" do
469
+ @resource.nil_resources.should_not be_empty
470
+ end
471
+
472
+ it "should side load the correct nil_resources" do
473
+ @resource.nil_resources.map(&:id).should == [2, 4]
474
+ end
475
+ end
476
+
477
+ context "id in side load" do
478
+ before(:each) do
479
+ ZendeskAPI::TestResource.has :nil_resource
480
+
481
+ stub_json_request(:get, %r{test_resources\?include=nil_resources}, json(
482
+ :test_resources => [{ :id => 1 }],
483
+ :nil_resources => [{ :id => 1, :test_resource_id => 2 }, { :id => 2, :test_resource_id => 1 }]
484
+ ))
485
+
486
+ subject.fetch(true)
487
+ @resource = subject.detect {|res| res.id == 1}
488
+ end
489
+
490
+ it "should side load nil_resources" do
491
+ @resource.nil_resource.should_not be_nil
492
+ end
493
+
494
+ it "should side load the correct nil_resources" do
495
+ @resource.nil_resource.id.should == 2
496
+ end
497
+ end
498
+
499
+ context "with name as key" do
500
+ before(:each) do
501
+ ZendeskAPI::TestResource.has :nil_resource, :include_key => :name
502
+
503
+ stub_json_request(:get, %r{test_resources\?include=nil_resources}, json(
504
+ :test_resources => [{ :id => 1, :nil_resource_id => 4 }],
505
+ :nil_resources => [{ :name => 1 }, { :name => 4 }]
506
+ ))
507
+
508
+ subject.fetch(true)
509
+
510
+ @resource = subject.detect {|res| res.id == 1}
511
+ end
512
+
513
+ it "should side load nil_resources" do
514
+ @resource.nil_resource.should_not be_nil
515
+ end
516
+
517
+ it "should side load the correct nil_resource" do
518
+ @resource.nil_resource.name.should == 4
519
+ end
520
+ end
521
+
522
+ context "sub-loading" do
523
+ before(:each) do
524
+ ZendeskAPI::TestResource.has :test_child
525
+ ZendeskAPI::TestResource::TestChild.has :nil_resource
526
+
527
+ stub_json_request(:get, %r{test_resources\?include=nil_resources}, json(
528
+ :test_resources => [{ :id => 1, :test_child => { :nil_resource_id => 4 } }],
529
+ :nil_resources => [{ :id => 1 }, { :id => 4 }]
530
+ ))
531
+
532
+ subject.fetch(true)
533
+
534
+ @resource = subject.detect {|res| res.id == 1}.test_child
535
+ end
536
+
537
+ it "should side load nil_resources" do
538
+ @resource.nil_resource.should_not be_nil
539
+ end
540
+
541
+ it "should side load the correct nil_resource" do
542
+ @resource.nil_resource.id.should == 4
543
+ end
544
+ end
545
+ end
546
+
404
547
  context "method missing" do
405
548
  before(:each) { subject.stub(:fetch).and_return([1, 2, nil, 3]) }
406
549
 
@@ -2,6 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe String do
4
4
  specify "the plural of forum if forums" do
5
- "forum".plural.should == "forums"
5
+ Inflection.plural("forum").should == "forums"
6
6
  end
7
7
  end
@@ -5,12 +5,14 @@ describe ZendeskAPI::ReadResource do
5
5
  let(:id) { 1 }
6
6
  subject { ZendeskAPI::TestResource }
7
7
 
8
- before(:each) do
9
- stub_json_request(:get, %r{test_resources/#{id}}, json("test_resource" => {}))
10
- end
8
+ context "normal request" do
9
+ before(:each) do
10
+ stub_json_request(:get, %r{test_resources/#{id}}, json("test_resource" => {}))
11
+ end
11
12
 
12
- it "should return instance of resource" do
13
- subject.find(client, :id => id).should be_instance_of(subject)
13
+ it "should return instance of resource" do
14
+ subject.find(client, :id => id).should be_instance_of(subject)
15
+ end
14
16
  end
15
17
 
16
18
  it "should blow up without an id which would build an invalid url" do
@@ -19,6 +21,22 @@ describe ZendeskAPI::ReadResource do
19
21
  }.to raise_error("No :id given")
20
22
  end
21
23
 
24
+ context "with side loads" do
25
+ before(:each) do
26
+ stub_json_request(:get, %r{test_resources/#{id}\?include=nil_resource}, json(
27
+ "test_resource" => { :id => 1, :nil_resource_id => 2 },
28
+ "nil_resources" => [{ :id => 1, :name => :bye }, { :id => 2, :name => :hi }]
29
+ ))
30
+
31
+ subject.has :nil_resource
32
+ @resource = subject.find(client, :id => id, :include => :nil_resource)
33
+ end
34
+
35
+ it "should side load nil resource" do
36
+ @resource.nil_resource.name.should == "hi"
37
+ end
38
+ end
39
+
22
40
  context "with client error" do
23
41
  it "should handle 500 properly" do
24
42
  stub_request(:get, %r{test_resources/#{id}}).to_return(:status => 500)
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe ZendeskAPI::Search do
4
+ context ".new" do
5
+ context "when given an existing class" do
6
+ it "should return the correct class" do
7
+ ZendeskAPI::Search.new(nil, { "result_type" => "user" }).should be_instance_of(ZendeskAPI::User)
8
+ end
9
+ end
10
+
11
+ context "when given a nonexistant class" do
12
+ it "should return an object of the type Search::Result" do
13
+ ZendeskAPI::Search.new(nil, { "result_type" => "blah" }).should be_instance_of(ZendeskAPI::Search::Result)
14
+ end
15
+ end
16
+
17
+ context "when not given anything" do
18
+ it "should return an object of the type Search::Result" do
19
+ ZendeskAPI::Search.new(nil, {}).should be_instance_of(ZendeskAPI::Search::Result)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,4 +1,3 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
2
1
  $:.unshift(File.join(File.dirname(__FILE__), "macros"))
3
2
 
4
3
  ENV['TZ'] = 'CET' # something that is not local and not utc so we find all the bugs
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zendesk_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-10-05 00:00:00.000000000 Z
13
+ date: 2012-10-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -228,8 +228,7 @@ files:
228
228
  - lib/zendesk_api/collection.rb
229
229
  - lib/zendesk_api/configuration.rb
230
230
  - lib/zendesk_api/core_ext/inflection.rb
231
- - lib/zendesk_api/core_ext/modulize.rb
232
- - lib/zendesk_api/core_ext/snakecase.rb
231
+ - lib/zendesk_api/helpers.rb
233
232
  - lib/zendesk_api/lru_cache.rb
234
233
  - lib/zendesk_api/middleware/request/etag_cache.rb
235
234
  - lib/zendesk_api/middleware/request/retry.rb
@@ -237,14 +236,15 @@ files:
237
236
  - lib/zendesk_api/middleware/response/callback.rb
238
237
  - lib/zendesk_api/middleware/response/deflate.rb
239
238
  - lib/zendesk_api/middleware/response/gzip.rb
239
+ - lib/zendesk_api/middleware/response/logger.rb
240
240
  - lib/zendesk_api/middleware/response/parse_iso_dates.rb
241
241
  - lib/zendesk_api/rescue.rb
242
242
  - lib/zendesk_api/resource.rb
243
243
  - lib/zendesk_api/resources/forum.rb
244
244
  - lib/zendesk_api/resources/misc.rb
245
- - lib/zendesk_api/resources/playlist.rb
246
245
  - lib/zendesk_api/resources/ticket.rb
247
246
  - lib/zendesk_api/resources/user.rb
247
+ - lib/zendesk_api/sideloading.rb
248
248
  - lib/zendesk_api/track_changes.rb
249
249
  - lib/zendesk_api/trackie.rb
250
250
  - lib/zendesk_api/verbs.rb
@@ -291,6 +291,7 @@ files:
291
291
  - spec/fixtures/credentials.yml.example
292
292
  - spec/fixtures/test_resources.rb
293
293
  - spec/fixtures/zendesk.rb
294
+ - spec/inflection_spec.rb
294
295
  - spec/lru_cache_spec.rb
295
296
  - spec/macros/resource_macros.rb
296
297
  - spec/middleware/request/etag_cache_spec.rb
@@ -301,12 +302,11 @@ files:
301
302
  - spec/middleware/response/deflate_spec.rb
302
303
  - spec/middleware/response/gzip_spec.rb
303
304
  - spec/middleware/response/parse_iso_dates_spec.rb
304
- - spec/playlist_spec.rb
305
305
  - spec/read_resource_spec.rb
306
306
  - spec/rescue_spec.rb
307
307
  - spec/resource_spec.rb
308
+ - spec/search_spec.rb
308
309
  - spec/spec_helper.rb
309
- - spec/string_spec.rb
310
310
  - spec/trackie_spec.rb
311
311
  - zendesk_api.gemspec
312
312
  homepage: http://developer.zendesk.com
@@ -346,6 +346,7 @@ test_files:
346
346
  - spec/fixtures/credentials.yml.example
347
347
  - spec/fixtures/test_resources.rb
348
348
  - spec/fixtures/zendesk.rb
349
+ - spec/inflection_spec.rb
349
350
  - spec/lru_cache_spec.rb
350
351
  - spec/macros/resource_macros.rb
351
352
  - spec/middleware/request/etag_cache_spec.rb
@@ -356,11 +357,10 @@ test_files:
356
357
  - spec/middleware/response/deflate_spec.rb
357
358
  - spec/middleware/response/gzip_spec.rb
358
359
  - spec/middleware/response/parse_iso_dates_spec.rb
359
- - spec/playlist_spec.rb
360
360
  - spec/read_resource_spec.rb
361
361
  - spec/rescue_spec.rb
362
362
  - spec/resource_spec.rb
363
+ - spec/search_spec.rb
363
364
  - spec/spec_helper.rb
364
- - spec/string_spec.rb
365
365
  - spec/trackie_spec.rb
366
366
  has_rdoc:
@@ -1,10 +0,0 @@
1
- # From https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/modulize.rb
2
- class String
3
- def modulize
4
- #gsub('__','/'). # why was this ever here?
5
- gsub(/__(.?)/){ "::#{$1.upcase}" }.
6
- gsub(/\/(.?)/){ "::#{$1.upcase}" }.
7
- gsub(/(?:_+|-+)([a-z])/){ $1.upcase }.
8
- gsub(/(\A|\s)([a-z])/){ $1 + $2.upcase }
9
- end
10
- end
@@ -1,12 +0,0 @@
1
- # From https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/snakecase.rb
2
- class String
3
- def snakecase
4
- #gsub(/::/, '/').
5
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
6
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
7
- tr('-', '_').
8
- gsub(/\s/, '_').
9
- gsub(/__+/, '_').
10
- downcase
11
- end
12
- end
@@ -1,64 +0,0 @@
1
- module ZendeskAPI
2
- class Playlist
3
- include Rescue
4
-
5
- attr_reader :ticket
6
- attr_accessor :id
7
-
8
- def initialize(client, id)
9
- @client, @id = client, id
10
- @ticket = nil
11
-
12
- @initialized = false
13
- @destroyed = false
14
-
15
- init_playlist
16
- end
17
-
18
- def each
19
- init_playlist unless initialized?
20
-
21
- while initialized? && !destroyed? && (n = self.next)
22
- yield n
23
- end
24
- end
25
-
26
- def next
27
- init_playlist unless initialized?
28
- return false if !initialized? || destroyed?
29
-
30
- response = @client.connection.get("play/next")
31
-
32
- if response.status == 200
33
- @ticket = Ticket.new(@client, response.body["ticket"])
34
- @ticket
35
- else
36
- @destroyed = (response.status == 204)
37
- nil
38
- end
39
- end
40
-
41
- def destroy
42
- response = @client.connection.delete("play")
43
- @destroyed = response.status == 204
44
- end
45
-
46
- def destroyed?
47
- @destroyed
48
- end
49
-
50
- def initialized?
51
- @initialized
52
- end
53
-
54
- private
55
-
56
- def init_playlist
57
- response = @client.connection.get("views/#{id}/play")
58
- @initialized = response.status == 302
59
- end
60
-
61
- rescue_client_error :next, :init_playlist
62
- rescue_client_error :destroy, :with => false
63
- end
64
- end
@@ -1,95 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ZendeskAPI::Playlist do
4
- subject { ZendeskAPI::Playlist }
5
-
6
- before(:each) do
7
- stub_request(:get, %r{views/\d+/play}).to_return(:status => 302, :body => "You are being redirected...")
8
- end
9
-
10
- it "should begin playing the playlist on initialization" do
11
- subject.new(client, 1)
12
- end
13
-
14
- context "#next" do
15
- subject { ZendeskAPI::Playlist.new(client, 1) }
16
-
17
- before(:each) do
18
- stub_json_request(:get, %r{play/next}, json("ticket" => {}))
19
- end
20
-
21
- it "should return ticket" do
22
- subject.next.should be_instance_of(ZendeskAPI::Ticket)
23
- end
24
-
25
- context "with client error", :silence_logger do
26
- before(:each) do
27
- stub_request(:get, %r{play/next}).to_return(:status => 500)
28
- end
29
-
30
- it "should be properly handled" do
31
- expect { subject.next.should be_nil }.to_not raise_error
32
- end
33
- end
34
-
35
- context "with end of playlist" do
36
- before(:each) do
37
- stub_request(:get, %r{play/next}).to_return(:status => 204)
38
- end
39
-
40
- it "should be properly handled" do
41
- subject.next.should be_nil
42
- subject.destroyed?.should be_true
43
- end
44
- end
45
- end
46
-
47
- context "#destroy" do
48
- subject { ZendeskAPI::Playlist.new(client, 1) }
49
-
50
- before(:each) do
51
- stub_request(:delete, %r{play}).to_return(:status => 204)
52
- end
53
-
54
- it "should be destroyed" do
55
- subject.destroy.should be_true
56
- subject.destroyed?.should be_true
57
- end
58
-
59
- context "with client error", :silence_logger do
60
- before(:each) do
61
- stub_request(:delete, %r{play}).to_return(:status => 500)
62
- end
63
-
64
- it "should be properly handled" do
65
- expect { subject.destroy.should be_false }.to_not raise_error
66
- end
67
- end
68
- end
69
-
70
- context "initialization" do
71
- context "with client error", :silence_logger do
72
- before(:each) do
73
- stub_request(:get, %r{views/\d+/play}).to_return(:status => 500).to_return(:status => 302)
74
- stub_request(:get, %r{play/next}).to_return(:body => json)
75
- end
76
-
77
- it "should be able to be created" do
78
- new_playlist = subject.new(client, 1)
79
- new_playlist.should_not be_nil
80
- end
81
-
82
- it "should retry initialization on #next" do
83
- new_playlist = subject.new(client, 1)
84
- new_playlist.should_receive(:init_playlist).and_return(:true)
85
- new_playlist.next
86
- end
87
-
88
- it "should retry initialization on #each" do
89
- new_playlist = subject.new(client, 1)
90
- new_playlist.should_receive(:next).and_return(Object.new, nil)
91
- new_playlist.each {|arg| :block }
92
- end
93
- end
94
- end
95
- end