zendesk_api 0.1.5 → 0.1.6

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.
@@ -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