hubspot-ruby 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/hubspot-ruby.gemspec +42 -5
  3. data/lib/hubspot-ruby.rb +3 -0
  4. data/lib/hubspot/blog.rb +14 -48
  5. data/lib/hubspot/connection.rb +95 -0
  6. data/lib/hubspot/contact.rb +74 -91
  7. data/lib/hubspot/contact_list.rb +127 -0
  8. data/lib/hubspot/contact_properties.rb +12 -0
  9. data/lib/hubspot/deal.rb +29 -25
  10. data/lib/hubspot/exceptions.rb +2 -0
  11. data/lib/hubspot/form.rb +88 -4
  12. data/lib/hubspot/topic.rb +8 -24
  13. data/lib/hubspot/utils.rb +0 -51
  14. data/lib/hubspot/version.rb +1 -1
  15. data/spec/fixtures/vcr_cassettes/add_contacts_to_lists.yml +281 -0
  16. data/spec/fixtures/vcr_cassettes/contact_create.yml +34 -2
  17. data/spec/fixtures/vcr_cassettes/contact_create_existing_email.yml +34 -2
  18. data/spec/fixtures/vcr_cassettes/contact_create_invalid_email.yml +35 -3
  19. data/spec/fixtures/vcr_cassettes/contact_create_with_params.yml +34 -2
  20. data/spec/fixtures/vcr_cassettes/contact_destroy.yml +36 -4
  21. data/spec/fixtures/vcr_cassettes/contact_find_by_email_batch_mode.yml +509 -0
  22. data/spec/fixtures/vcr_cassettes/contact_find_by_id_batch_mode.yml +33 -0
  23. data/spec/fixtures/vcr_cassettes/contact_find_by_utk_batch_mode.yml +33 -0
  24. data/spec/fixtures/vcr_cassettes/contact_list_batch_find.yml +65 -0
  25. data/spec/fixtures/vcr_cassettes/contact_list_destroy.yml +63 -0
  26. data/spec/fixtures/vcr_cassettes/contact_list_example.yml +33 -0
  27. data/spec/fixtures/vcr_cassettes/contact_list_find.yml +96 -0
  28. data/spec/fixtures/vcr_cassettes/contact_list_refresh.yml +33 -0
  29. data/spec/fixtures/vcr_cassettes/contact_list_update.yml +36 -0
  30. data/spec/fixtures/vcr_cassettes/contacts_among_list.yml +189 -0
  31. data/spec/fixtures/vcr_cassettes/create_form.yml +39 -0
  32. data/spec/fixtures/vcr_cassettes/create_list.yml +36 -0
  33. data/spec/fixtures/vcr_cassettes/create_list_with_filters.yml +36 -0
  34. data/spec/fixtures/vcr_cassettes/deal_create.yml +29 -0
  35. data/spec/fixtures/vcr_cassettes/deal_find.yml +56 -0
  36. data/spec/fixtures/vcr_cassettes/destroy_deal.yml +110 -0
  37. data/spec/fixtures/vcr_cassettes/fail_to_create_form.yml +35 -0
  38. data/spec/fixtures/vcr_cassettes/fail_to_create_list.yml +35 -0
  39. data/spec/fixtures/vcr_cassettes/field_among_form.yml +34 -0
  40. data/spec/fixtures/vcr_cassettes/fields_among_form.yml +35 -0
  41. data/spec/fixtures/vcr_cassettes/find_all_contacts.yml +39 -0
  42. data/spec/fixtures/vcr_cassettes/find_all_dynamic_lists.yml +104 -0
  43. data/spec/fixtures/vcr_cassettes/find_all_forms.yml +15378 -0
  44. data/spec/fixtures/vcr_cassettes/find_all_lists.yml +138 -0
  45. data/spec/fixtures/vcr_cassettes/find_all_recent_contacts.yml +33 -0
  46. data/spec/fixtures/vcr_cassettes/find_all_stastic_lists.yml +21876 -0
  47. data/spec/fixtures/vcr_cassettes/form_destroy.yml +64 -0
  48. data/spec/fixtures/vcr_cassettes/form_example.yml +39 -0
  49. data/spec/fixtures/vcr_cassettes/form_find.yml +69 -0
  50. data/spec/fixtures/vcr_cassettes/form_submit_data.yml +130 -0
  51. data/spec/fixtures/vcr_cassettes/form_update.yml +77 -0
  52. data/spec/fixtures/vcr_cassettes/one_month_blog_posts_filter_state.yml +5168 -0
  53. data/spec/fixtures/vcr_cassettes/remove_contacts_from_lists.yml +315 -0
  54. data/spec/lib/hubspot/blog_spec.rb +4 -3
  55. data/spec/lib/hubspot/connection_spec.rb +112 -0
  56. data/spec/lib/hubspot/contact_list_spec.rb +249 -0
  57. data/spec/lib/hubspot/contact_properties_spec.rb +8 -0
  58. data/spec/lib/hubspot/contact_spec.rb +110 -54
  59. data/spec/lib/hubspot/deal_spec.rb +11 -0
  60. data/spec/lib/hubspot/form_spec.rb +157 -10
  61. data/spec/lib/hubspot/utils_spec.rb +0 -67
  62. data/spec/live/deals_integration_spec.rb +35 -0
  63. data/spec/spec_helper.rb +2 -0
  64. data/spec/support/tests_helper.rb +17 -0
  65. metadata +70 -33
@@ -0,0 +1,127 @@
1
+ module Hubspot
2
+ #
3
+ # HubSpot Contact lists API
4
+ #
5
+ class ContactList
6
+ LISTS_PATH = '/contacts/v1/lists'
7
+ LIST_PATH = '/contacts/v1/lists/:list_id'
8
+ LIST_BATCH_PATH = LISTS_PATH + '/batch'
9
+ CONTACTS_PATH = LIST_PATH + '/contacts/all'
10
+ RECENT_CONTACTS_PATH = LIST_PATH + '/contacts/recent'
11
+ ADD_CONTACT_PATH = LIST_PATH + '/add'
12
+ REMOVE_CONTACT_PATH = LIST_PATH + '/remove'
13
+ REFRESH_PATH = LIST_PATH + '/refresh'
14
+
15
+ class << self
16
+ # {http://developers.hubspot.com/docs/methods/lists/create_list}
17
+ def create!(opts={})
18
+ dynamic = opts.delete(:dynamic) { false }
19
+ portal_id = opts.delete(:portal_id) { Hubspot::Config.portal_id }
20
+
21
+ response = Hubspot::Connection.post_json(LISTS_PATH, params: {}, body: opts.merge({ dynamic: dynamic, portal_id: portal_id}) )
22
+ new(response)
23
+ end
24
+
25
+ # {http://developers.hubspot.com/docs/methods/lists/get_lists}
26
+ # {http://developers.hubspot.com/docs/methods/lists/get_static_lists}
27
+ # {http://developers.hubspot.com/docs/methods/lists/get_dynamic_lists}
28
+ def all(opts={})
29
+ static = opts.delete(:static) { false }
30
+ dynamic = opts.delete(:dynamic) { false }
31
+
32
+ # NOTE: As opposed of what the documentation says, getting the static or dynamic lists returns all the lists, not only 20 lists
33
+ path = LISTS_PATH + (static ? '/static' : dynamic ? '/dynamic' : '')
34
+ response = Hubspot::Connection.get_json(path, opts)
35
+ response['lists'].map { |l| new(l) }
36
+ end
37
+
38
+ # {http://developers.hubspot.com/docs/methods/lists/get_list}
39
+ # {http://developers.hubspot.com/docs/methods/lists/get_batch_lists}
40
+ def find(ids)
41
+ batch_mode, path, params = case ids
42
+ when Integer then [false, LIST_PATH, { list_id: ids }]
43
+ when String then [false, LIST_PATH, { list_id: ids.to_i }]
44
+ when Array then [true, LIST_BATCH_PATH, { batch_list_id: ids.map(&:to_i) }]
45
+ else raise Hubspot::InvalidParams, 'expecting Integer or Array of Integers parameter'
46
+ end
47
+
48
+ response = Hubspot::Connection.get_json(path, params)
49
+ batch_mode ? response['lists'].map { |l| new(l) } : new(response)
50
+ end
51
+ end
52
+
53
+ attr_reader :id
54
+ attr_reader :portal_id
55
+ attr_reader :name
56
+ attr_reader :dynamic
57
+ attr_reader :properties
58
+
59
+ def initialize(hash)
60
+ self.send(:assign_properties, hash)
61
+ end
62
+
63
+ # {http://developers.hubspot.com/docs/methods/lists/update_list}
64
+ def update!(opts={})
65
+ response = Hubspot::Connection.post_json(LIST_PATH, params: { list_id: @id }, body: opts)
66
+ self.send(:assign_properties, response)
67
+ self
68
+ end
69
+
70
+ # {http://developers.hubspot.com/docs/methods/lists/delete_list}
71
+ def destroy!
72
+ response = Hubspot::Connection.delete_json(LIST_PATH, { list_id: @id })
73
+ @destroyed = (response.code == 204)
74
+ end
75
+
76
+ # {http://developers.hubspot.com/docs/methods/lists/get_list_contacts}
77
+ def contacts(opts={})
78
+ # NOTE: caching functionality can be dependant of the nature of the list, if dynamic or not ...
79
+ bypass_cache = opts.delete(:bypass_cache) { false }
80
+ recent = opts.delete(:recent) { false }
81
+
82
+ if bypass_cache || @contacts.nil?
83
+ path = recent ? RECENT_CONTACTS_PATH : CONTACTS_PATH
84
+ opts[:list_id] = @id
85
+
86
+ response = Hubspot::Connection.get_json(path, Hubspot::ContactProperties.add_default_parameters(opts))
87
+ @contacts = response['contacts'].map { |c| Hubspot::Contact.new(c) }
88
+ else
89
+ @contacts
90
+ end
91
+ end
92
+
93
+ # {http://developers.hubspot.com/docs/methods/lists/refresh_list}
94
+ def refresh
95
+ response = Hubspot::Connection.post_json(REFRESH_PATH, params: { list_id: @id, no_parse: true }, body: {})
96
+ response.code == 204
97
+ end
98
+
99
+ # {http://developers.hubspot.com/docs/methods/lists/add_contact_to_list}
100
+ def add(contacts)
101
+ contact_ids = [contacts].flatten.uniq.compact.map(&:vid)
102
+ response = Hubspot::Connection.post_json(ADD_CONTACT_PATH, params: { list_id: @id }, body: { vids: contact_ids })
103
+ response['updated'].sort == contact_ids.sort
104
+ end
105
+
106
+ # {http://developers.hubspot.com/docs/methods/lists/remove_contact_from_list}
107
+ def remove(contacts)
108
+ contact_ids = [contacts].flatten.uniq.compact.map(&:vid)
109
+ response = Hubspot::Connection.post_json(REMOVE_CONTACT_PATH, params: { list_id: @id }, body: { vids: contact_ids })
110
+ response['updated'].sort == contact_ids.sort
111
+ end
112
+
113
+ def destroyed?
114
+ !!@destroyed
115
+ end
116
+
117
+ private
118
+
119
+ def assign_properties(hash)
120
+ @id = hash['listId']
121
+ @portal_id = hash['portalId']
122
+ @name = hash['name']
123
+ @dynamic = hash['dynamic']
124
+ @properties = hash
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,12 @@
1
+ module Hubspot
2
+ class ContactProperties
3
+ class << self
4
+ # TODO: properties can be set as configuration
5
+ # TODO: find the way how to set a list of Properties + merge same property key if present from opts
6
+ def add_default_parameters(opts={})
7
+ properties = 'email'
8
+ opts.merge(property: properties)
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/hubspot/deal.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'hubspot/utils'
2
- require 'httparty'
3
2
 
4
3
  module Hubspot
5
4
  #
@@ -8,6 +7,10 @@ module Hubspot
8
7
  # {http://developers.hubspot.com/docs/methods/deals/deals_overview}
9
8
  #
10
9
  class Deal
10
+ CREATE_DEAL_PATH = "/deals/v1/deal"
11
+ DEAL_PATH = "/deals/v1/deal/:deal_id"
12
+ RECENT_UPDATED_PATH = "/deals/v1/deal/recent/modified"
13
+ UPDATE_DEAL_PATH = '/deals/v1/deal/:deal_id'
11
14
 
12
15
  attr_reader :properties
13
16
  attr_reader :portal_id
@@ -15,10 +18,6 @@ module Hubspot
15
18
  attr_reader :company_ids
16
19
  attr_reader :vids
17
20
 
18
- CREATE_DEAL_PATH = "/deals/v1/deal"
19
- DEAL_PATH = "/deals/v1/deal/:deal_id"
20
- RECENT_UPDATED_PATH = "/deals/v1/deal/recent/modified"
21
-
22
21
  def initialize(response_hash)
23
22
  @portal_id = response_hash["portalId"]
24
23
  @deal_id = response_hash["dealId"]
@@ -29,21 +28,17 @@ module Hubspot
29
28
 
30
29
  class << self
31
30
  def create!(portal_id, company_ids, vids, params={})
32
- url = Hubspot::Utils.generate_url(CREATE_DEAL_PATH).concat("&portalId=#{portal_id}")
31
+ #TODO: clean following hash, Hubspot::Utils should do the trick
33
32
  associations_hash = {"portalId" => portal_id, "associations" => { "associatedCompanyIds" => company_ids, "associatedVids" => vids}}
34
33
  post_data = associations_hash.merge({ properties: Hubspot::Utils.hash_to_properties(params, key_name: "name") })
35
- resp = HTTParty.post(url, body: post_data.to_json, headers: {"Content-Type" => "application/json"})
36
- Hubspot::Deal.new(resp.parsed_response)
34
+
35
+ response = Hubspot::Connection.post_json(CREATE_DEAL_PATH, params: {}, body: post_data )
36
+ new(response)
37
37
  end
38
38
 
39
39
  def find(deal_id)
40
- url = Hubspot::Utils.generate_url(DEAL_PATH, {deal_id: deal_id})
41
- resp = HTTParty.get(url, format: :json)
42
- if resp.success?
43
- Hubspot::Deal.new(resp.parsed_response)
44
- else
45
- nil
46
- end
40
+ response = Hubspot::Connection.get_json(DEAL_PATH, { deal_id: deal_id })
41
+ new(response)
47
42
  end
48
43
 
49
44
  # Find recent updated deals.
@@ -51,28 +46,37 @@ module Hubspot
51
46
  # @param count [Integer] the amount of deals to return.
52
47
  # @param offset [Integer] pages back through recent contacts.
53
48
  def recent(opts = {})
54
- url = Hubspot::Utils.generate_url(RECENT_UPDATED_PATH, opts)
55
- request = HTTParty.get(url, format: :json)
56
-
57
- raise(Hubspot::RequestError.new(request)) unless request.success?
58
-
59
- found = request.parsed_response['results']
60
- return found.map{|h| new(h) }
49
+ response = Hubspot::Connection.get_json(RECENT_UPDATED_PATH, opts)
50
+ response['results'].map { |d| new(d) }
61
51
  end
52
+
62
53
  end
63
54
 
64
55
  # Archives the contact in hubspot
65
56
  # {https://developers.hubspot.com/docs/methods/contacts/delete_contact}
66
57
  # @return [TrueClass] true
67
58
  def destroy!
68
- url = Hubspot::Utils.generate_url(DEAL_PATH, {deal_id: deal_id})
69
- request = HTTParty.delete(url, format: :json)
70
- raise(Hubspot::RequestError.new(request)) unless request.success?
59
+ response = Hubspot::Connection.delete_json(DEAL_PATH, {deal_id: deal_id})
71
60
  @destroyed = true
72
61
  end
73
62
 
74
63
  def destroyed?
75
64
  !!@destroyed
76
65
  end
66
+
67
+ def [](property)
68
+ @properties[property]
69
+ end
70
+
71
+ # Updates the properties of a deal
72
+ # {https://developers.hubspot.com/docs/methods/deals/update_deal}
73
+ # @param params [Hash] hash of properties to update
74
+ # @return [Hubspot::Deal] self
75
+ def update!(params)
76
+ query = {"properties" => Hubspot::Utils.hash_to_properties(params.stringify_keys!, key_name: 'name')}
77
+ response = Hubspot::Connection.put_json(UPDATE_DEAL_PATH, params: { deal_id: deal_id }, body: query)
78
+ @properties.merge!(params)
79
+ self
80
+ end
77
81
  end
78
82
  end
@@ -13,4 +13,6 @@ module Hubspot
13
13
  class ConfigurationError < StandardError; end
14
14
  class MissingInterpolation < StandardError; end
15
15
  class ContactExistsError < RequestError; end
16
+ class InvalidParams < StandardError; end
17
+ class ApiError < StandardError; end
16
18
  end
data/lib/hubspot/form.rb CHANGED
@@ -1,11 +1,95 @@
1
1
  module Hubspot
2
+ #
3
+ # HubSpot Form API
4
+ #
5
+ # {https://developers.hubspot.com/docs/methods/forms/forms_overview}
6
+ #
2
7
  class Form
3
- def initialize(form_guid)
4
- @form_guid = form_guid
8
+ FORMS_PATH = '/contacts/v1/forms'
9
+ FORM_PATH = '/contacts/v1/forms/:form_guid'
10
+ FIELDS_PATH = '/contacts/v1/fields/:form_guid'
11
+ FIELD_PATH = FIELDS_PATH + '/:field_name'
12
+ SUBMIT_DATA_PATH = '/uploads/form/v2/:portal_id/:form_guid'
13
+
14
+ class << self
15
+ # {https://developers.hubspot.com/docs/methods/forms/create_form}
16
+ def create!(opts={})
17
+ response = Hubspot::Connection.post_json(FORMS_PATH, params: {}, body: opts)
18
+ new(response)
19
+ end
20
+
21
+ def all
22
+ response = Hubspot::Connection.get_json(FORMS_PATH, {})
23
+ response.map { |f| new(f) }
24
+ end
25
+
26
+ # {https://developers.hubspot.com/docs/methods/forms/get_form}
27
+ def find(guid)
28
+ response = Hubspot::Connection.get_json(FORM_PATH, { form_guid: guid })
29
+ new(response)
30
+ end
31
+ end
32
+
33
+ attr_reader :guid
34
+ attr_reader :fields
35
+ attr_reader :properties
36
+
37
+ def initialize(hash)
38
+ self.send(:assign_properties, hash)
39
+ end
40
+
41
+ # {https://developers.hubspot.com/docs/methods/forms/get_fields}
42
+ # {https://developers.hubspot.com/docs/methods/forms/get_field}
43
+ def fields(opts={})
44
+ bypass_cache = opts.delete(:bypass_cache) { false }
45
+ field_name = opts.delete(:only) { nil }
46
+
47
+ if field_name
48
+ field_name = field_name.to_s
49
+ if bypass_cache || @fields.nil? || @fields.empty?
50
+ response = Hubspot::Connection.get_json(FIELD_PATH, { form_guid: @guid, field_name: field_name })
51
+ response
52
+ else
53
+ @fields.detect { |f| f['name'] == field_name }
54
+ end
55
+ else
56
+ if bypass_cache || @fields.nil? || @fields.empty?
57
+ response = Hubspot::Connection.get_json(FIELDS_PATH, { form_guid: @guid })
58
+ @fields = response
59
+ end
60
+ @fields
61
+ end
5
62
  end
63
+
64
+ # {https://developers.hubspot.com/docs/methods/forms/submit_form}
65
+ def submit(opts={})
66
+ response = Hubspot::FormsConnection.submit(SUBMIT_DATA_PATH, params: { form_guid: @guid }, body: opts)
67
+ [204, 302].include?(response.code)
68
+ end
69
+
70
+ # {https://developers.hubspot.com/docs/methods/forms/update_form}
71
+ def update!(opts={})
72
+ response = Hubspot::Connection.post_json(FORM_PATH, params: { form_guid: @guid }, body: opts)
73
+ self.send(:assign_properties, response)
74
+ self
75
+ end
76
+
77
+ # {https://developers.hubspot.com/docs/methods/forms/delete_form}
78
+ def destroy!
79
+ response = Hubspot::Connection.delete_json(FORM_PATH, { form_guid: @guid })
80
+ @destroyed = (response.code == 204)
81
+ end
82
+
83
+ def destroyed?
84
+ !!@destroyed
85
+ end
86
+
87
+ private
6
88
 
7
- def url
8
- Hubspot::Utils.generate_url("/uploads/form/v2/:portal_id/:form_guid", {form_guid: @form_guid}, {base_url: "https://forms.hubspot.com", hapikey: false})
89
+ def assign_properties(hash)
90
+ @guid = hash['guid']
91
+ @fields = hash['fields']
92
+ @properties = hash
9
93
  end
10
94
  end
11
95
  end
data/lib/hubspot/topic.rb CHANGED
@@ -1,42 +1,26 @@
1
- require 'hubspot/utils'
2
- require 'httparty'
3
-
4
1
  module Hubspot
5
2
  #
6
3
  # HubSpot Topics API
7
4
  #
8
5
  class Topic
9
- TOPIC_LIST_PATH = "/content/api/v2/topics"
10
- GET_TOPIC_BY_ID_PATH = "/content/api/v2/topics/:topic_id"
6
+ TOPICS_PATH = "/content/api/v2/topics"
7
+ TOPIC_PATH = "/content/api/v2/topics/:topic_id"
11
8
 
12
9
  class << self
13
10
  # Lists the topics
14
11
  # {https://developers.hubspot.com/docs/methods/blogv2/get_topics)
15
- # @return [Hubspot::Topic, []] array of topics or empty_array
12
+ # @return [Hubspot::Topic] array of topics
16
13
  def list
17
- url = Hubspot::Utils.generate_url(TOPIC_LIST_PATH)
18
- resp = HTTParty.get(url, format: :json)
19
- if resp.success?
20
- resp.parsed_response['objects'].map do |topic_hash|
21
- Topic.new(topic_hash)
22
- end
23
- else
24
- []
25
- end
14
+ response = Hubspot::Connection.get_json(TOPICS_PATH, {})
15
+ response['objects'].map { |t| new(t) }
26
16
  end
27
17
 
28
18
  # Finds the details for a specific topic_id
29
19
  # {https://developers.hubspot.com/docs/methods/blogv2/get_topics_topic_id }
30
- # @return Hubspot::Topic or nil
31
-
20
+ # @return Hubspot::Topic
32
21
  def find_by_topic_id(id)
33
- url = Hubspot::Utils.generate_url(GET_TOPIC_BY_ID_PATH, topic_id: id)
34
- resp = HTTParty.get(url, format: :json)
35
- if resp.success?
36
- Topic.new(resp.parsed_response)
37
- else
38
- nil
39
- end
22
+ response = Hubspot::Connection.get_json(TOPIC_PATH, { topic_id: id })
23
+ new(response)
40
24
  end
41
25
  end
42
26
 
data/lib/hubspot/utils.rb CHANGED
@@ -13,57 +13,6 @@ module Hubspot
13
13
  key_name = opts[:key_name] || "property"
14
14
  hash.map{ |k,v| { key_name => k.to_s, "value" => v}}
15
15
  end
16
-
17
- # Generate the API URL for the request
18
- #
19
- # @param path [String] The path of the request with leading "/". Parts starting with a ":" will be interpolated
20
- # @param params [Hash] params to be included in the query string or interpolated into the url.
21
- #
22
- # @return [String]
23
- #
24
- def generate_url(path, params={}, options={})
25
- Hubspot::Config.ensure! :hapikey
26
- path = path.clone
27
- params = params.clone
28
- base_url = options[:base_url] || Hubspot::Config.base_url
29
- params["hapikey"] = Hubspot::Config.hapikey unless options[:hapikey] == false
30
-
31
- if path =~ /:portal_id/
32
- Hubspot::Config.ensure! :portal_id
33
- params["portal_id"] = Hubspot::Config.portal_id if path =~ /:portal_id/
34
- end
35
-
36
- params.each do |k,v|
37
- if path.match(":#{k}")
38
- path.gsub!(":#{k}",v.to_s)
39
- params.delete(k)
40
- end
41
- end
42
- raise(Hubspot::MissingInterpolation.new("Interpolation not resolved")) if path =~ /:/
43
- query = params.map{ |k,v| param_string(k,v) }.join("&")
44
- path += "?" if query.present?
45
- base_url + path + query
46
- end
47
-
48
-
49
- private
50
-
51
- def converted_value(value)
52
- if (value.is_a?(Time))
53
- (value.to_i * 1000) # convert into milliseconds since epoch
54
- else
55
- value
56
- end
57
- end
58
-
59
- def param_string(key,value)
60
- if (key =~ /range/)
61
- raise "Value must be a range" unless value.is_a?(Range)
62
- "#{key}=#{converted_value(value.begin)}&#{key}=#{converted_value(value.end)}"
63
- else
64
- "#{key}=#{converted_value(value)}"
65
- end
66
- end
67
16
  end
68
17
  end
69
18
  end