boblail-unfuddle 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6c4a223f7cf345cfab592d4b71aa877cb04bbbe7
4
+ data.tar.gz: fb9e1a041a454022f5c779743ef5c6df879f7fd9
5
+ SHA512:
6
+ metadata.gz: 7c0da95ccb04a123c6553cfe44a488c8a8f562aa954a79ec0f26277f6ed549a48e2072d49fbbe3f28a33ffd4eb6029892f247e8d8e808d849dc36ce22b1339b7
7
+ data.tar.gz: 7810194c1b509809f2031c3ba061978936e56d8f2e30483ab56b444e28e2d4ecda9b7697d39bcd248bcad4fda8f924a3301adf1033fb69d18771b89c3aa1dab6
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in hiccup.gemspec
4
+ gemspec
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "lib"
6
+ t.libs << "test"
7
+ t.pattern = "test/**/*_test.rb"
8
+ t.verbose = false
9
+ end
@@ -0,0 +1,171 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'active_support/core_ext/benchmark'
4
+ require 'active_support/core_ext/hash'
5
+ require 'active_support/core_ext/hash/indifferent_access'
6
+ require 'unfuddle/configuration'
7
+ require 'unfuddle/error'
8
+ require 'unfuddle/project'
9
+ require 'unfuddle/person'
10
+ require 'unfuddle/response'
11
+ require 'unfuddle/has_tickets'
12
+
13
+
14
+ class Unfuddle
15
+ include HasTickets
16
+
17
+ class << self
18
+
19
+ def config(options=nil)
20
+ configuration = Unfuddle::Configuration.new
21
+ configuration.from_options(options) if options
22
+ yield configuration if block_given?
23
+ instance.configuration = configuration
24
+ end
25
+
26
+ def with_config(options)
27
+ current_configuration = instance.configuration
28
+ begin
29
+ config(current_configuration.merge(options))
30
+ yield
31
+ ensure
32
+ instance.configuration = current_configuration
33
+ end
34
+ end
35
+
36
+ def instance
37
+ @unfuddle ||= self.new
38
+ end
39
+
40
+ def assert_response!(expected_response_code, response)
41
+ unless response.status == expected_response_code
42
+ raise InvalidResponseError.new(response)
43
+ end
44
+ end
45
+
46
+ # Homemade `delegate`
47
+ [:get, :post, :put, :delete, :configuration].each do |method|
48
+ module_eval <<-RUBY
49
+ def #{method}(*args, &block)
50
+ instance.#{method}(*args, &block)
51
+ end
52
+ RUBY
53
+ end
54
+
55
+ [:subdomain, :username, :password, :logger, :include_associations?, :include_closed_on?, :timeout].each do |method|
56
+ module_eval <<-RUBY
57
+ def #{method}
58
+ configuration.#{method}
59
+ end
60
+ RUBY
61
+ end
62
+
63
+ end
64
+
65
+ attr_reader :configuration
66
+
67
+ def configuration=(configuration)
68
+ @configuration = configuration
69
+ @http = nil
70
+ end
71
+
72
+ [:subdomain, :username, :password, :logger, :include_associations?, :include_closed_on?, :timeout].each do |method|
73
+ module_eval <<-RUBY
74
+ def #{method}
75
+ configuration.#{method}
76
+ end
77
+ RUBY
78
+ end
79
+
80
+
81
+
82
+ def projects
83
+ Unfuddle.Project.all
84
+ end
85
+
86
+ def project(project_id)
87
+ Unfuddle::Project.new("id" => project_id)
88
+ end
89
+
90
+ def people(options={})
91
+ Unfuddle::Person.all(options)
92
+ end
93
+
94
+ def person(person_id)
95
+ Unfuddle::Person.new("id" => person_id)
96
+ end
97
+
98
+
99
+
100
+ def get(path)
101
+ http_send_with_logging :get, path
102
+ end
103
+
104
+ def post(path, object)
105
+ http_send_with_logging :post, path, object.to_xml
106
+ end
107
+
108
+ def put(path, object)
109
+ http_send_with_logging :put, path, object.to_xml
110
+ end
111
+
112
+ def delete(path)
113
+ http_send_with_logging :delete, path
114
+ end
115
+
116
+
117
+
118
+ def configured?
119
+ subdomain && username && password
120
+ end
121
+
122
+
123
+
124
+ protected
125
+
126
+ def http_send_with_logging(method, path, body=nil, headers={})
127
+ path = "/api/v1/#{path}"
128
+ headers = headers.merge({ # read JSON, write XML
129
+ "Accept" => "application/json",
130
+ "Content-type" => "application/xml" })
131
+
132
+ response = nil
133
+ ms = Benchmark.ms do
134
+ response = http_send(method, path, body, headers)
135
+ end
136
+ response
137
+ ensure
138
+ message = "[unfuddle:#{method}] #{path}"
139
+ message << "\n #{body}" if body
140
+ message = '%s (%.1fms)' % [ message, ms ] if ms
141
+ logger.info(message)
142
+
143
+ url = http.url_prefix.to_s + path
144
+ logger.warn "[unfuddle:#{method}] URL length exceeded 2000 characters" if url.length > 2000
145
+ end
146
+
147
+ def http_send(method, path, body, headers)
148
+ response = http.public_send(method, path, body, headers) do |req|
149
+ req.options[:timeout] = timeout
150
+ end
151
+
152
+ Response.new(response).tap do |response|
153
+ raise ServerError.new(response) if response.server_error?
154
+ raise UnauthorizedError.new(response) if response.unauthorized?
155
+ end
156
+
157
+ rescue Faraday::Error::ConnectionFailed
158
+ raise ConnectionError, $!.message
159
+ rescue Faraday::Error::TimeoutError, Errno::ETIMEDOUT
160
+ raise TimeoutError, $!.message
161
+ end
162
+
163
+ def http
164
+ raise ConfigurationError, "Unfuddle is not configured" unless configured?
165
+ @http ||= Faraday.new("https://#{subdomain}.unfuddle.com", ssl: {verify: false}) do |faraday|
166
+ faraday.adapter Faraday.default_adapter
167
+ faraday.basic_auth username, password
168
+ end
169
+ end
170
+
171
+ end
@@ -0,0 +1,223 @@
1
+ require 'unfuddle'
2
+ require 'active_support/inflector'
3
+ require 'active_support/core_ext/hash'
4
+
5
+
6
+ class Unfuddle
7
+ class Base
8
+
9
+ def initialize(attributes)
10
+ __set_attributes(attributes)
11
+ end
12
+
13
+ def unfetched?
14
+ @attributes.keys == %w{id}
15
+ end
16
+
17
+ def id
18
+ @attributes["id"]
19
+ end
20
+
21
+ def __set_attributes(attributes)
22
+ raise ArgumentError, "attributes is expected to be a hash, but it was #{attributes.class} instead" unless attributes.is_a?(Hash)
23
+ if @attributes
24
+ @attributes.merge! attributes
25
+ else
26
+ @attributes = attributes
27
+ end
28
+ end
29
+
30
+
31
+
32
+ # Respond to attributes
33
+
34
+ def attributes
35
+ _attributes.dup
36
+ end
37
+
38
+ def method_missing(method_name, *args, &block)
39
+ if has_attribute?(method_name)
40
+ _attributes[method_name.to_s]
41
+ else
42
+ super(method_name, *args, &block)
43
+ end
44
+ end
45
+
46
+ def respond_to?(method_name)
47
+ super(method_name) || has_attribute?(method_name)
48
+ end
49
+
50
+ def has_attribute?(attribute_name)
51
+ _attributes.key?(attribute_name.to_s)
52
+ end
53
+
54
+ def write_attribute(attribute, value)
55
+ _attributes[attribute.to_s] = value
56
+ end
57
+
58
+ def update_attributes!(attributes)
59
+ attributes.each do |key, value|
60
+ write_attribute(key, value)
61
+ end
62
+ save!
63
+ end
64
+
65
+ def update_attribute(attribute, value)
66
+ write_attribute(attribute, value)
67
+ save!
68
+ end
69
+
70
+
71
+
72
+ # Finders
73
+
74
+ class << self
75
+
76
+ def find_by(*attributes)
77
+ attributes.each do |attribute|
78
+ instance_eval <<-RUBY
79
+ def find_by_#{attribute}(value)
80
+ all.find { |instance| instance.#{attribute} == value }
81
+ end
82
+ RUBY
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+
89
+
90
+ # Has many
91
+
92
+ class << self
93
+
94
+ def has_many(collection_name, options={})
95
+ collection_name = collection_name.to_s
96
+ path = collection_name
97
+ individual_name = collection_name.singularize
98
+ class_name = options.fetch(:class_name, individual_name.classify)
99
+
100
+ require "unfuddle/#{individual_name}"
101
+
102
+ class_eval <<-RUBY
103
+ def #{collection_name}
104
+ @#{collection_name} ||= get('#{path}').json.map { |attributes| #{class_name}.new(attributes) }
105
+ end
106
+
107
+ def #{individual_name}(id)
108
+ attributes = find_#{individual_name}(id)
109
+ #{class_name}.new(attributes) if attributes
110
+ end
111
+
112
+ def find_#{individual_name}(id)
113
+ response = get("#{path}/\#{id}")
114
+
115
+ return nil if response.status == 404
116
+ Unfuddle.assert_response!(200, response)
117
+
118
+ response.json
119
+ end
120
+
121
+ def new_#{individual_name}(attributes)
122
+ #{class_name}.new(attributes)
123
+ end
124
+
125
+ def create_#{individual_name}(params)
126
+ instance = #{class_name}.new(params)
127
+ response = post('#{path}', instance)
128
+ Unfuddle.assert_response!(201, response)
129
+
130
+ instance.__set_attributes(response.json)
131
+
132
+ unless instance.id
133
+ id = response.location.chomp("/")[/\\d+$/].to_i
134
+ instance.__set_attributes("id" => id) if id > 0
135
+ end
136
+
137
+ @#{collection_name}.push(instance) if @#{collection_name}
138
+ instance
139
+ end
140
+ RUBY
141
+ end
142
+
143
+ end
144
+
145
+
146
+
147
+ # Nest Unfuddle requests
148
+
149
+ def relative_path
150
+ raise NotImplementedError
151
+ end
152
+
153
+ [:get, :post, :put, :delete].each do |method|
154
+ module_eval <<-RUBY
155
+ def #{method}(path, *args)
156
+ Unfuddle.#{method}(relative_path + "/" + path, *args)
157
+ end
158
+ RUBY
159
+ end
160
+
161
+
162
+
163
+ # Serialization
164
+
165
+ def to_params
166
+ attributes
167
+ end
168
+
169
+ def singular_name
170
+ self.class.name[/[^:]*$/].tableize.singularize
171
+ end
172
+
173
+ def to_xml
174
+ to_params.to_xml(root: singular_name)
175
+ end
176
+
177
+ def to_json
178
+ JSON.dump({singular_name => to_params})
179
+ end
180
+
181
+ def fetch!
182
+ response = get(get_path)
183
+ Unfuddle.assert_response!(200, response)
184
+ __set_attributes(response.json)
185
+
186
+ rescue InvalidResponseError
187
+ binding.pry if binding.respond_to?(:pry)
188
+ raise $!
189
+ end
190
+
191
+ def save!
192
+ put put_path, self
193
+ end
194
+
195
+ def destroy!
196
+ delete delete_path
197
+ end
198
+
199
+
200
+
201
+ def get_path
202
+ ""
203
+ end
204
+
205
+ def put_path
206
+ ""
207
+ end
208
+
209
+ def delete_path
210
+ ""
211
+ end
212
+
213
+
214
+
215
+ private
216
+
217
+ def _attributes
218
+ fetch! if unfetched?
219
+ @attributes
220
+ end
221
+
222
+ end
223
+ end
@@ -0,0 +1,13 @@
1
+ require 'unfuddle/base'
2
+
3
+ class Unfuddle
4
+ class Comment < Base
5
+
6
+ def relative_path
7
+ "projects/#{project_id}/tickets/#{parent_id}/comments/#{id}"
8
+ end
9
+
10
+ attr_accessor :project_id
11
+
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require 'unfuddle/base'
2
+
3
+ class Unfuddle
4
+ class Component < Base
5
+
6
+ def relative_path
7
+ "projects/#{project_id}/components/#{id}"
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,67 @@
1
+ class Unfuddle
2
+ class Configuration
3
+
4
+
5
+ [:subdomain, :username, :password, :logger, :include_associations, :include_closed_on, :timeout].each do |attribute|
6
+ module_eval <<-RUBY
7
+ def #{attribute}(*arg)
8
+ set_value :#{attribute}, arg.first if arg.any?
9
+ get_value :#{attribute}
10
+ end
11
+
12
+ def #{attribute}=(value)
13
+ set_value :#{attribute}, value
14
+ end
15
+ RUBY
16
+ end
17
+
18
+
19
+ alias :include_associations? :include_associations
20
+ alias :include_closed_on? :include_closed_on
21
+
22
+
23
+ def merge(options)
24
+ to_h.merge(options)
25
+ end
26
+
27
+ def to_h
28
+ { subdomain: subdomain,
29
+ username: username,
30
+ password: password,
31
+ include_associations: include_associations,
32
+ include_closed_on: include_closed_on,
33
+ logged: logger,
34
+ timeout: timeout }
35
+ end
36
+
37
+ def from_options(options)
38
+ options = options.with_indifferent_access
39
+ self.subdomain = options[:subdomain] if options.key?(:subdomain)
40
+ self.username = options[:username] if options.key?(:username)
41
+ self.password = options[:password] if options.key?(:password)
42
+ self.include_associations = options[:include_associations] if options.key?(:include_associations)
43
+ self.include_closed_on = options[:include_closed_on] if options.key?(:include_closed_on)
44
+ self.timeout = options.fetch :timeout, 120
45
+ self.logger = options.fetch :logger, Unfuddle::Configuration::Logger.new
46
+ end
47
+
48
+
49
+ class Logger
50
+ def info(s)
51
+ puts s
52
+ end
53
+ end
54
+
55
+
56
+ private
57
+
58
+ def set_value(attribute, value)
59
+ instance_variable_set(:"@#{attribute}", value)
60
+ end
61
+
62
+ def get_value(attribute)
63
+ instance_variable_get(:"@#{attribute}")
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,11 @@
1
+ require 'unfuddle/base'
2
+
3
+ class Unfuddle
4
+ class CustomFieldValue < Base
5
+
6
+ def relative_path
7
+ "projects/#{project_id}/custom_field_values/#{id}"
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ class Unfuddle
2
+
3
+ module Error
4
+ end
5
+
6
+ class ConfigurationError < StandardError
7
+ include ::Unfuddle::Error
8
+ end
9
+
10
+ class ConnectionError < StandardError
11
+ include ::Unfuddle::Error
12
+ end
13
+
14
+ class TimeoutError < ConnectionError
15
+ end
16
+
17
+
18
+
19
+ class InvalidResponseError < StandardError
20
+ include ::Unfuddle::Error
21
+
22
+ def initialize(response)
23
+ @response = response
24
+ end
25
+
26
+ attr_reader :response
27
+
28
+ def message
29
+ response.body
30
+ end
31
+
32
+ end
33
+
34
+ class ServerError < InvalidResponseError
35
+ end
36
+
37
+ class UnauthorizedError < InvalidResponseError
38
+
39
+ def message
40
+ "The user '#{Unfuddle.instance.username}' does not have permission to access this resource"
41
+ end
42
+
43
+ end
44
+
45
+
46
+
47
+ class UndefinedCustomField < ArgumentError
48
+ include ::Unfuddle::Error
49
+ end
50
+
51
+ class UndefinedCustomFieldValue < ArgumentError
52
+ include ::Unfuddle::Error
53
+ end
54
+
55
+ class UndefinedSeverity < ArgumentError
56
+ include ::Unfuddle::Error
57
+ end
58
+
59
+ end
@@ -0,0 +1,68 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+
4
+ class Unfuddle
5
+ module HasTickets
6
+
7
+ def find_ticket_by_number(number)
8
+ response = get("tickets/by_number/#{number}")
9
+
10
+ return nil if response.status == 404
11
+ Unfuddle.assert_response!(200, response)
12
+
13
+ response.json
14
+ end
15
+
16
+ def find_tickets!(*conditions)
17
+ raise ArgumentError.new("No conditions supplied: that's probably not good") if conditions.none?
18
+ path = "ticket_reports/dynamic.json"
19
+ path << "?conditions_string=#{construct_ticket_query(*conditions)}"
20
+ fields = %w{number summary description reporter_email resolution milestone_id created_at due_on severity_id component_id}
21
+ fields << "associations" if Unfuddle.include_associations?
22
+ fields << "closed_on" if Unfuddle.include_closed_on?
23
+ path << "&fields_string=#{fields.join(",")}" if fields.any?
24
+ response = get(path)
25
+
26
+ Unfuddle.assert_response!(200, response)
27
+
28
+ ticket_report = response.json
29
+ group0 = ticket_report.fetch("groups", [])[0] || {}
30
+ group0.fetch("tickets", [])
31
+ end
32
+
33
+
34
+
35
+ def construct_ticket_query(*conditions)
36
+ conditions_string = []
37
+ conditions.each do |condition|
38
+ case condition
39
+ when Hash
40
+ conditions_string.concat(condition.map { |key, value| Array.wrap(value).map { |value| create_condition_string(key, value) }.join("|") })
41
+ when Array
42
+ key, value = condition
43
+ conditions_string.push(Array.wrap(value).map { |value| create_condition_string(key, value) }.join("|"))
44
+ else
45
+ conditions_string.push(condition)
46
+ end
47
+ end
48
+ conditions_string.join("%2C")
49
+ end
50
+
51
+ def create_condition_string(key, value)
52
+ comparison = "eq"
53
+ comparison, value = "neq", value.value if value.is_a?(Neq)
54
+ key, value = prepare_key_and_value_for_conditions_string(key, value)
55
+ "#{key}-#{comparison}-#{value}"
56
+ end
57
+
58
+ def prepare_key_and_value_for_conditions_string(key, value)
59
+
60
+ # If the value is an id, convert it to a number
61
+ value = value.to_i if value.is_a?(String) && value =~ /^\d+$/
62
+
63
+ [key, value]
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,11 @@
1
+ require 'unfuddle/base'
2
+
3
+ class Unfuddle
4
+ class Milestone < Base
5
+
6
+ def relative_path
7
+ "projects/#{project_id}/milestones/#{id}"
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ class Unfuddle
2
+ module NeqHelper
3
+
4
+ def neq(arg)
5
+ Neq.new(arg)
6
+ end
7
+
8
+ end
9
+
10
+ class Neq
11
+
12
+ def initialize(value)
13
+ @value = value
14
+ end
15
+
16
+ def to_s
17
+ "neq(#{value.inspect})"
18
+ end
19
+
20
+ attr_reader :value
21
+
22
+ end
23
+ end
24
+
@@ -0,0 +1,24 @@
1
+ require 'unfuddle/base'
2
+
3
+
4
+ class Unfuddle
5
+ class Person < Base
6
+
7
+ def self.all(options={})
8
+ url = options[:including_removed] == true ? "people.json?removed=true" : "people.json"
9
+ response = Unfuddle.get(url)
10
+
11
+ if response.status == 404
12
+ nil
13
+ else
14
+ Unfuddle.assert_response!(200, response)
15
+ response.json.map { |attributes| self.new(attributes) }
16
+ end
17
+ end
18
+
19
+ def relative_path
20
+ "people/#{id}"
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,136 @@
1
+ require 'unfuddle/base'
2
+ require 'unfuddle/has_tickets'
3
+
4
+
5
+ class Unfuddle
6
+ class Project < Base
7
+ include HasTickets
8
+
9
+ def self.all
10
+ @projects ||= begin
11
+ response = Unfuddle.get('projects')
12
+
13
+ if response.status == 404
14
+ nil
15
+ else
16
+ Unfuddle.assert_response!(200, response)
17
+ response.json.map { |attributes| self.new(attributes) }
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.fetch(id)
23
+ response = Unfuddle.get("projects/#{id}")
24
+
25
+ if response.status == 404
26
+ nil
27
+ else
28
+ Unfuddle.assert_response!(200, response)
29
+ self.new response.json
30
+ end
31
+ end
32
+
33
+ def relative_path
34
+ "projects/#{id}"
35
+ end
36
+
37
+
38
+
39
+ find_by :title
40
+
41
+ has_many :custom_field_values
42
+ has_many :severities
43
+ has_many :components
44
+ has_many :ticket_reports
45
+ has_many :tickets
46
+ has_many :milestones
47
+
48
+
49
+
50
+ def open_milestones
51
+ @open_milestones ||= get("projects/#{id}/milestones/upcoming").json.map { |attributes| Milestone.new(attributes) }
52
+ end
53
+
54
+
55
+
56
+ def custom_fields
57
+ { 1 => ticket_field1_active ? ticket_field1_title : nil,
58
+ 2 => ticket_field2_active ? ticket_field2_title : nil,
59
+ 3 => ticket_field3_active ? ticket_field3_title : nil }
60
+ end
61
+
62
+ def custom_fields_defined
63
+ custom_fields.values.compact
64
+ end
65
+
66
+ def custom_field_defined?(field_title)
67
+ custom_fields_defined.member?(field_title)
68
+ end
69
+
70
+ def first_available_custom_field
71
+ (1..3).find { |n| custom_fields[n].nil? }
72
+ end
73
+
74
+ def number_of_custom_field_named!(name)
75
+ number_of_custom_field_named(name) || (raise UndefinedCustomField, "A custom field named \"#{name}\" is not defined!")
76
+ end
77
+
78
+ def number_of_custom_field_named(name)
79
+ custom_fields.key(name)
80
+ end
81
+
82
+
83
+
84
+ def find_custom_field_value_by_value!(field, value)
85
+ n = number_of_custom_field_named!(field)
86
+ result = custom_field_values.find { |cfv| cfv.field_number == n && cfv.value == value }
87
+ raise UndefinedCustomFieldValue, "\"#{value}\" is not a value for the custom field \"#{field}\"!" unless result
88
+ result
89
+ end
90
+
91
+ def find_custom_field_value_by_id!(field, id)
92
+ n = number_of_custom_field_named!(field)
93
+ result = custom_field_values.find { |cfv| cfv.field_number == n && cfv.id == id }
94
+ raise UndefinedCustomFieldValue, "\"#{id}\" is not the id of a value for the custom field \"#{field}\"!" unless result
95
+ result
96
+ end
97
+
98
+
99
+
100
+ def find_severity_by_name!(name)
101
+ find_severity_by_name(name) || (raise UndefinedSeverity, "A severity named \"#{name}\" is not defined!")
102
+ end
103
+
104
+ def find_severity_by_name(name)
105
+ severities.find { |severity| severity.name == name }
106
+ end
107
+
108
+
109
+
110
+ def prepare_key_and_value_for_conditions_string(key, value)
111
+ key, value = super
112
+
113
+ # If the value is the name of a severity, try to look it up
114
+ value = find_severity_by_name!(value).id if key == :severity && value.is_a?(String)
115
+
116
+ # If the key is a custom field, look up the key and value
117
+ if key.is_a?(String)
118
+ value = find_custom_field_value_by_value!(key, value).id unless value.is_a?(Fixnum)
119
+ key = get_key_for_custom_field_named!(key)
120
+ end
121
+
122
+ [key, value]
123
+ end
124
+
125
+ def get_key_for_custom_field_named!(name)
126
+ "field_#{number_of_custom_field_named!(name)}"
127
+ end
128
+
129
+ def get_ticket_attribute_for_custom_value_named!(name)
130
+ "field#{number_of_custom_field_named!(name)}_value_id"
131
+ end
132
+
133
+
134
+
135
+ end
136
+ end
@@ -0,0 +1,49 @@
1
+ require 'unfuddle/error'
2
+
3
+
4
+ class Unfuddle
5
+ class Response
6
+
7
+
8
+ def initialize(faraday_response)
9
+ @faraday_response = faraday_response
10
+ end
11
+
12
+ def status
13
+ faraday_response.status
14
+ end
15
+
16
+ def server_error?
17
+ status == 500
18
+ end
19
+
20
+ def unauthorized?
21
+ status == 401
22
+ end
23
+
24
+ def body
25
+ faraday_response.body
26
+ end
27
+
28
+ def location
29
+ faraday_response["location"]
30
+ end
31
+
32
+ def json
33
+ @json ||= begin
34
+ json = self.class.normalized_body(body)
35
+ json.empty? ? {} : JSON.load(json)
36
+ end
37
+ end
38
+
39
+ def self.normalized_body(body)
40
+ body.gsub(/\u0000/, "").strip
41
+ end
42
+
43
+
44
+ private
45
+
46
+ attr_reader :faraday_response
47
+
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ require 'unfuddle/base'
2
+
3
+ class Unfuddle
4
+ class Severity < Base
5
+
6
+ def relative_path
7
+ "projects/#{project_id}/severities/#{id}"
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ require 'unfuddle/base'
2
+
3
+ class Unfuddle
4
+ class Ticket < Base
5
+
6
+ def relative_path
7
+ "projects/#{project_id}/tickets/#{id}"
8
+ end
9
+
10
+ has_many :comments
11
+
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require 'unfuddle/base'
2
+
3
+ class Unfuddle
4
+ class TicketReport < Base
5
+
6
+ def relative_path
7
+ "projects/#{parent_id}/ticket_reports/#{id}"
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ class Unfuddle
2
+ VERSION = "0.7.0"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "rubygems"
2
+ require "simplecov"
3
+ require "rails"
4
+ require "rails/test_help"
5
+ require "active_support/core_ext"
6
+ require "turn"
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "unfuddle/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "boblail-unfuddle"
7
+ s.version = Unfuddle::VERSION
8
+ s.authors = ["Bob Lail"]
9
+ s.email = ["bob.lailfamily@gmail.com"]
10
+
11
+ s.homepage = "http://boblail.github.com/unfuddle/"
12
+ s.summary = %q{A library for communicating with Unfuddle}
13
+ s.description = %q{A library for communicating with Unfuddle}
14
+
15
+ s.add_dependency "activesupport"
16
+ s.add_dependency "builder"
17
+ s.add_dependency "faraday", "~> 0.8.8"
18
+
19
+ s.add_development_dependency "rails"
20
+ s.add_development_dependency "turn"
21
+ s.add_development_dependency "simplecov"
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: boblail-unfuddle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - Bob Lail
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: builder
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.8.8
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.8.8
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: turn
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: A library for communicating with Unfuddle
98
+ email:
99
+ - bob.lailfamily@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - Gemfile
106
+ - Gemfile.lock
107
+ - Rakefile
108
+ - lib/unfuddle.rb
109
+ - lib/unfuddle/base.rb
110
+ - lib/unfuddle/comment.rb
111
+ - lib/unfuddle/component.rb
112
+ - lib/unfuddle/configuration.rb
113
+ - lib/unfuddle/custom_field_value.rb
114
+ - lib/unfuddle/error.rb
115
+ - lib/unfuddle/has_tickets.rb
116
+ - lib/unfuddle/milestone.rb
117
+ - lib/unfuddle/neq.rb
118
+ - lib/unfuddle/person.rb
119
+ - lib/unfuddle/project.rb
120
+ - lib/unfuddle/response.rb
121
+ - lib/unfuddle/severity.rb
122
+ - lib/unfuddle/ticket.rb
123
+ - lib/unfuddle/ticket_report.rb
124
+ - lib/unfuddle/version.rb
125
+ - test/test_helper.rb
126
+ - unfuddle.gemspec
127
+ homepage: http://boblail.github.com/unfuddle/
128
+ licenses: []
129
+ metadata: {}
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubyforge_project:
146
+ rubygems_version: 2.2.2
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: A library for communicating with Unfuddle
150
+ test_files:
151
+ - test/test_helper.rb