intercom 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ doc
1
2
  *.gem
2
3
  *.iml
3
4
  .bundle
data/changes.txt ADDED
@@ -0,0 +1,15 @@
1
+ 0.0.5
2
+ - added Intercom::User.find_by_email, Intercom::User.find_by_user_id
3
+ - add support for Intercom::User.all.each, Intercom::User.all.map, Intercom::User.all.count
4
+
5
+ 0.0.4
6
+ - allow to directly set custom_data hash on Intercom::User user.custom_data = {...}
7
+
8
+ 0.0.3
9
+ - renamed Intercom.secret_key to Intercom.api_key for consistency throughout our app and docs
10
+
11
+ 0.0.2
12
+ - updates to reflect changes to resources served by the api
13
+
14
+ 0.0.1
15
+ - experimental version
data/lib/intercom.rb CHANGED
@@ -67,7 +67,7 @@ module Intercom
67
67
  end
68
68
 
69
69
  def self.execute_request(method, path, params = {}, headers = {}, payload = nil)
70
- method.eql?(:get) ? require_email_or_user_id(params) : require_email_or_user_id(payload)
70
+ method.eql?(:get) ? require_email_or_user_id(params) : require_email_or_user_id(payload) unless path.eql?("users")
71
71
  args =rest_client_args(method, path, params, headers, payload)
72
72
  begin
73
73
  response = RestClient::Request.execute(args)
data/lib/intercom/user.rb CHANGED
@@ -1,11 +1,26 @@
1
1
  require 'intercom/user_resource'
2
- require 'intercom/shallow_hash'
2
+ require 'intercom/user_custom_data'
3
+ require 'intercom/user_collection_proxy'
3
4
  require 'intercom/social_profile'
4
5
 
5
6
  module Intercom
6
- ##
7
7
  # Represents a user of your application on Intercom.
8
- class User < UserResource
8
+ #
9
+ # == Example usage
10
+ # * Fetching a user
11
+ # Intercom::User.find_by_email("bob@example.")
12
+ #
13
+ # * Getting the count of all users
14
+ # Intercom::User.all.count
15
+ #
16
+ # * Fetching all users
17
+ # Intercom::User.all.each {|user| puts user.email }
18
+ #
19
+ # * Updating custom data on a user
20
+ # user = Intercom::User.find_by_email("bob@example.com")
21
+ # user.custom_data["number_of_applications"] = 11
22
+ # user.save
23
+ class User < UserResource
9
24
  ##
10
25
  # Fetches an Intercom::User from our API.
11
26
  #
@@ -19,7 +34,26 @@ module Intercom
19
34
  User.from_api(response)
20
35
  end
21
36
 
22
- ##
37
+ # Calls GET https://api.intercom.io/v1/users?email=EMAIL
38
+ #
39
+ # returns Intercom::User object representing the state on our servers.
40
+ #
41
+ # @param [String] email address of the user
42
+ # @return [User]
43
+ def self.find_by_email(email)
44
+ find({:email => email})
45
+ end
46
+
47
+ # Calls GET https://api.intercom.io/v1/users?user_id=USER-ID
48
+ #
49
+ # returns Intercom::User object representing the state on our servers.
50
+ #
51
+ # @param [String] user_id user id of the user
52
+ # @return [User]
53
+ def self.find_by_user_id(user_id)
54
+ find({:user_id => user_id})
55
+ end
56
+
23
57
  # Creates (or updates when a user already exists for that email/user_id) a user record on your application.
24
58
  #
25
59
  # Calls POST https://api.intercom.io/v1/users
@@ -32,6 +66,22 @@ module Intercom
32
66
  User.new(params).save
33
67
  end
34
68
 
69
+ # Retrieve all the users
70
+ # Examples:
71
+ # Intercom::User.all.count
72
+ # > 5346
73
+ # Intercom::User.each do |user|
74
+ # puts user.inspect
75
+ # end
76
+ # > ["user1@example.com" ,"user2@example.com" ,....]
77
+ # Intercom::User.map(&:email)
78
+ # > ["user1@example.com" ,"user2@example.com" ,....]
79
+ #
80
+ # @return [UserCollectionProxy]
81
+ def self.all
82
+ UserCollectionProxy.new
83
+ end
84
+
35
85
  # instance method alternative to #create
36
86
  # @return [User]
37
87
  def save
@@ -39,11 +89,13 @@ module Intercom
39
89
  self.update_from_api_response(response)
40
90
  end
41
91
 
42
- # @return {User}
92
+ # @return [String] the {User}'s name
43
93
  def name
44
94
  @attributes["name"]
45
95
  end
46
96
 
97
+ # @param [String] name {User}'s name
98
+ # @return [void]
47
99
  def name=(name)
48
100
  @attributes["name"]=name
49
101
  end
@@ -53,6 +105,7 @@ module Intercom
53
105
  @attributes["last_seen_ip"]
54
106
  end
55
107
 
108
+ # @return [void]
56
109
  def last_seen_ip=(last_seen_ip)
57
110
  @attributes["last_seen_ip"]=last_seen_ip
58
111
  end
@@ -62,6 +115,7 @@ module Intercom
62
115
  @attributes["last_seen_user_agent"]
63
116
  end
64
117
 
118
+ # @return [void]
65
119
  def last_seen_user_agent=(last_seen_user_agent)
66
120
  @attributes["last_seen_user_agent"]=last_seen_user_agent
67
121
  end
@@ -92,6 +146,7 @@ module Intercom
92
146
 
93
147
  ##
94
148
  # Set Time at which this User started using your application.
149
+ # @return [void]
95
150
  def created_at=(time)
96
151
  set_time_at("created_at", time)
97
152
  end
@@ -120,17 +175,30 @@ module Intercom
120
175
  @location_data ||= {}.freeze
121
176
  end
122
177
 
123
- ##
124
- # Get hash of custom attributes stored for this Intercom::User
178
+ # Custom attributes stored for this Intercom::User
125
179
  #
126
180
  # See http://docs.intercom.io/#CustomData for more information
127
- # @return [Hash]
181
+ #
182
+ # Example: Reading custom_data value for an existing user
183
+ # user = Intercom::User.find(:email => "someone@example.com")
184
+ # puts user.custom_data[:plan]
185
+ #
186
+ # Example: Setting some custom data for an existing user
187
+ # user = Intercom::User.find(:email => "someone@example.com")
188
+ # user.custom_data[:plan] = "pro"
189
+ # user.save
190
+ #
191
+ # @return [UserCustomData]
128
192
  def custom_data
129
- @attributes["custom_data"] ||= ShallowHash.new
193
+ @attributes["custom_data"] ||= UserCustomData.new
130
194
  end
131
195
 
132
- def custom_data=(custom_data) #:nodoc:
133
- @attributes["custom_data"] = ShallowHash.new.merge(custom_data)
196
+ # Set a {Hash} of custom data attributes to save/update on this user
197
+ #
198
+ # @param [Hash] custom_data
199
+ # @return [UserCustomData]
200
+ def custom_data=(custom_data)
201
+ @attributes["custom_data"] = UserCustomData.new(custom_data)
134
202
  end
135
203
 
136
204
  protected
@@ -0,0 +1,51 @@
1
+ require 'intercom/user_resource'
2
+ require 'intercom/user_custom_data'
3
+ require 'intercom/social_profile'
4
+
5
+ module Intercom
6
+ # A collection of your Users from Intercom
7
+ # Uses the paginated users api under the covers (http://docs.intercom.io/api#getting_all_users)
8
+ #
9
+ # == Examples:
10
+ #
11
+ # Fetching a count of all Users tracked on Intercom
12
+ # Intercom::User.all.count
13
+ #
14
+ # Iterating over each user
15
+ # Intercom::User.each do |user|
16
+ # puts user.inspect
17
+ # end
18
+ #
19
+ class UserCollectionProxy
20
+ # @return [Integer] number of users tracked on Intercom for this application
21
+ def count
22
+ response = Intercom.get("users", {:per_page => 1})
23
+ response["total_count"]
24
+ end
25
+
26
+ # yields each {User} to the block provided
27
+ # @return [void]
28
+ def each(&block)
29
+ page = 1
30
+ fetch_another_page = true
31
+ while fetch_another_page
32
+ current_page = Intercom.get("users", {:page => page})
33
+ current_page["users"].each do |user|
34
+ block.call User.from_api(user)
35
+ end
36
+ page = page + 1
37
+ fetch_another_page = !current_page["next_page"].nil?
38
+ end
39
+ end
40
+
41
+ # yields each {User} to the block provided and collects the output in the same way as Enumerable#map
42
+ # @return [Array<Object>]
43
+ def map
44
+ out = []
45
+ each { |e| out << yield(e) }
46
+ out
47
+ end
48
+
49
+ alias :collect :map
50
+ end
51
+ end
@@ -0,0 +1,27 @@
1
+ module Intercom
2
+ # Sub-class of {Hash} for storing custom data attributes.
3
+ # Doesn't allow nested Hashes or Arrays. And requires {String} or {Symbol} keys.
4
+ class UserCustomData < Hash
5
+ def initialize(attributes={})
6
+ (attributes).each do |key, value|
7
+ validate_key_and_value(key, value)
8
+ self[key] = value
9
+ end
10
+ end
11
+
12
+ def []=(key, value)
13
+ validate_key_and_value(key, value)
14
+ super(key.to_s, value)
15
+ end
16
+
17
+ def [](key)
18
+ super(key.to_s)
19
+ end
20
+
21
+ private
22
+ def validate_key_and_value(key, value)
23
+ raise ArgumentError.new("custom_data does not support nested data structures (key: #{key}, value: #{value}") if value.is_a?(Array) || value.is_a?(Hash)
24
+ raise ArgumentError.new("custom_data key must be String or Symbol: #{key}") unless key.is_a?(String) || key.is_a?(Symbol)
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Intercom #:nodoc:
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+
3
+ describe Intercom::UserCollectionProxy do
4
+ before :each do
5
+ Intercom.expects(:execute_request).never
6
+ end
7
+
8
+ it "supports each" do
9
+ Intercom.expects(:get).with("users", {:page => 1}).returns(page_of_users)
10
+ emails = []
11
+ Intercom::User.all.each { |user| emails << user.email }
12
+ emails.must_equal %W(user1@example.com user2@example.com user3@example.com)
13
+ end
14
+
15
+ it "supports map" do
16
+ Intercom.expects(:get).with("users", {:page => 1}).returns(page_of_users).twice
17
+ Intercom::User.all.map { |user| user.email }.must_equal %W(user1@example.com user2@example.com user3@example.com)
18
+ Intercom::User.all.collect { |user| user.email }.must_equal %W(user1@example.com user2@example.com user3@example.com)
19
+ end
20
+
21
+ it "yields each user to the block" do
22
+ Intercom.expects(:get).with("users", {:per_page => 1}).returns(page_of_users(1,1))
23
+ Intercom::User.all.count.must_equal 3
24
+ end
25
+
26
+ it "loads multiple pages" do
27
+ Intercom.expects(:get).with("users", {:page => 1}).returns(page_of_users(1, 1))
28
+ Intercom.expects(:get).with("users", {:page => 2}).returns(page_of_users(2, 1))
29
+ Intercom.expects(:get).with("users", {:page => 3}).returns(page_of_users(3, 1))
30
+ Intercom::User.all.map { |user| user.email }.must_equal %W(user1@example.com user2@example.com user3@example.com)
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe Intercom::UserCustomData do
4
+ it "raises if you try to set or merge in nested hash structures" do
5
+ data = Intercom::UserCustomData.new()
6
+ proc { data["thing"] = [1] }.must_raise ArgumentError
7
+ proc { data["thing"] = {1 => 2} }.must_raise ArgumentError
8
+ proc { Intercom::UserCustomData.new({1 => {2 => 3}}) }.must_raise ArgumentError
9
+ end
10
+
11
+ it "raises if you try to use a non string key" do
12
+ data = Intercom::UserCustomData.new()
13
+ proc { data[1] = "something" }.must_raise ArgumentError
14
+ end
15
+
16
+ it "sets and merges valid entries" do
17
+ data = Intercom::UserCustomData.new()
18
+ data["a"] = 1
19
+ data[:b] = 2
20
+ data[:a].must_equal 1
21
+ data["b"].must_equal 2
22
+ data[:b].must_equal 2
23
+ data = Intercom::UserCustomData.new({"a" => 1, :b => 2})
24
+ data["a"].must_equal 1
25
+ data[:a].must_equal 1
26
+ data["b"].must_equal 2
27
+ data[:b].must_equal 2
28
+ end
29
+ end
@@ -77,6 +77,7 @@ describe "Intercom::User" do
77
77
  user = Intercom::User.new()
78
78
  proc { user.custom_data["thing"] = [1] }.must_raise ArgumentError
79
79
  proc { user.custom_data["thing"] = {1 => 2} }.must_raise ArgumentError
80
+ proc { user.custom_data = {1 => {2 => 3}} }.must_raise ArgumentError
80
81
 
81
82
  user = Intercom::User.from_api(test_user)
82
83
  proc { user.custom_data["thing"] = [1] }.must_raise ArgumentError
@@ -85,7 +86,7 @@ describe "Intercom::User" do
85
86
  it "fetches a user" do
86
87
  Intercom.expects(:get).with("users", {"email" => "bo@example.com"}).returns(test_user)
87
88
  user = Intercom::User.find("email" => "bo@example.com")
88
- user.email.must_equal "bo@example.com"
89
+ user.email.must_equal "bob@example.com"
89
90
  user.name.must_equal "Joe Schmoe"
90
91
  user.session_count.must_equal 123
91
92
  end
@@ -121,7 +122,7 @@ describe "Intercom::User" do
121
122
  params = {"email" => "me@example.com", :user_id => "abc123", "name" => "Bob Smith", "last_seen_ip" => "1.2.3.4", "last_seen_user_agent" => "ie6", "created_at" => Time.now}
122
123
  user = Intercom::User.new(params)
123
124
  user.to_hash.keys.sort.must_equal params.keys.map(&:to_s).sort
124
- params.keys.each do | key|
125
+ params.keys.each do |key|
125
126
  user.send(key).to_s.must_equal params[key].to_s
126
127
  end
127
128
  end
@@ -130,4 +131,20 @@ describe "Intercom::User" do
130
131
  user = Intercom::User.send(:from_api, {"new_param" => "some value"})
131
132
  user.new_param.must_equal "some value"
132
133
  end
134
+
135
+ it "returns a UserCollectionProxy for all without making any requests" do
136
+ Intercom.expects(:execute_request).never
137
+ all = Intercom::User.all
138
+ all.must_be_instance_of(Intercom::UserCollectionProxy)
139
+ end
140
+
141
+ it "can find_by_email" do
142
+ Intercom::User.expects(:find).with(:email => "bob@example.com")
143
+ Intercom::User.find_by_email("bob@example.com")
144
+ end
145
+
146
+ it "can find_by_user_id" do
147
+ Intercom::User.expects(:find).with(:user_id => "abc123")
148
+ Intercom::User.find_by_user_id("abc123")
149
+ end
133
150
  end
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
 
3
3
  describe Intercom do
4
4
  it "has a version number" do
5
- Intercom::VERSION.must_match /\d+\.\d+\.\d+/
5
+ Intercom::VERSION.must_match(/\d+\.\d+\.\d+/)
6
6
  end
7
7
 
8
8
  describe "API" do
@@ -47,9 +47,9 @@ describe Intercom do
47
47
  it "checks for email or user id" do
48
48
  proc { Intercom.require_email_or_user_id("else") }.must_raise ArgumentError, "Expected params Hash, got String"
49
49
  proc { Intercom.require_email_or_user_id(:something => "else") }.must_raise ArgumentError, "Either email or user_id must be specified"
50
- proc { Intercom.get("users", :something => "else") }.must_raise ArgumentError, "Either email or user_id must be specified"
51
- proc { Intercom.put("users", :something => "else") }.must_raise ArgumentError, "Either email or user_id must be specified"
52
- proc { Intercom.post("users", :something => "else") }.must_raise ArgumentError, "Either email or user_id must be specified"
50
+ proc { Intercom.get("messages", :something => "else") }.must_raise ArgumentError, "Either email or user_id must be specified"
51
+ proc { Intercom.put("messages", :something => "else") }.must_raise ArgumentError, "Either email or user_id must be specified"
52
+ proc { Intercom.post("messages", :something => "else") }.must_raise ArgumentError, "Either email or user_id must be specified"
53
53
  Intercom.require_email_or_user_id(:email => "bob@example.com", :something => "else")
54
54
  Intercom.require_email_or_user_id("email" => "bob@example.com", :something => "else")
55
55
  Intercom.require_email_or_user_id(:user_id => "123")
@@ -2,16 +2,16 @@ require 'intercom'
2
2
  require 'minitest/autorun'
3
3
  require 'mocha'
4
4
 
5
- def test_user
5
+ def test_user(email="bob@example.com")
6
6
  {
7
7
  :user_id => 'id-from-customers-app',
8
- :email => "bo@example.com",
8
+ :email => email,
9
9
  :name => "Joe Schmoe",
10
10
  :created_at => 1323422442,
11
- :last_seen_ip => "1.2.3.4",
12
- :last_seen_user_agent => "Mozilla blah blah ie6",
11
+ :last_seen_ip => "1.2.3.4",
12
+ :last_seen_user_agent => "Mozilla blah blah ie6",
13
13
  :custom_data => {"a" => "b", "b" => 2},
14
- :relationship_score => 90,
14
+ :relationship_score => 90,
15
15
  :session_count => 123,
16
16
  :last_impression_at => 1323422442,
17
17
  :social_profiles => [
@@ -71,6 +71,22 @@ def test_message
71
71
  }
72
72
  end
73
73
 
74
+ def page_of_users(page=1, per_page=10)
75
+ all_users = [test_user("user1@example.com"), test_user("user2@example.com"), test_user("user3@example.com")]
76
+ offset = (page - 1) * per_page
77
+ limit = page * per_page
78
+ next_page = limit < all_users.size ? page + 1 : nil
79
+ previous_page = offset > 0 ? page - 1 : nil
80
+ {
81
+ "users" => all_users[offset..limit-1],
82
+ "total_count" => all_users.size,
83
+ "page" => page,
84
+ "next_page" => next_page,
85
+ "previous_page" => previous_page,
86
+ "total_pages" => (all_users.size.to_f / per_page).ceil.to_i
87
+ }
88
+ end
89
+
74
90
  def capture_exception(&block)
75
91
  begin
76
92
  block.call
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: intercom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-03-09 00:00:00.000000000Z
14
+ date: 2012-03-13 00:00:00.000000000Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rest-client
18
- requirement: &70301913505580 !ruby/object:Gem::Requirement
18
+ requirement: &70170925085820 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,10 +23,10 @@ dependencies:
23
23
  version: '0'
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *70301913505580
26
+ version_requirements: *70170925085820
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
- requirement: &70301913504840 !ruby/object:Gem::Requirement
29
+ requirement: &70170925084400 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ! '>='
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: '0'
35
35
  type: :development
36
36
  prerelease: false
37
- version_requirements: *70301913504840
37
+ version_requirements: *70170925084400
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: rake
40
- requirement: &70301913504220 !ruby/object:Gem::Requirement
40
+ requirement: &70170925083440 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: '0'
46
46
  type: :development
47
47
  prerelease: false
48
- version_requirements: *70301913504220
48
+ version_requirements: *70170925083440
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: mocha
51
- requirement: &70301913503620 !ruby/object:Gem::Requirement
51
+ requirement: &70170925082380 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ! '>='
@@ -56,7 +56,7 @@ dependencies:
56
56
  version: '0'
57
57
  type: :development
58
58
  prerelease: false
59
- version_requirements: *70301913503620
59
+ version_requirements: *70170925082380
60
60
  description: ! 'Intercom (https://www.intercom.io) is a customer relationship management
61
61
  and messaging tool for web app owners. This library wraps the api provided by Intercom.
62
62
  See http://docs.intercom.io/api for more details. '
@@ -73,21 +73,24 @@ files:
73
73
  - LICENSE
74
74
  - README.rdoc
75
75
  - Rakefile
76
- - changed.txt
76
+ - changes.txt
77
77
  - intercom.gemspec
78
78
  - lib/data/cacert.pem
79
79
  - lib/intercom.rb
80
80
  - lib/intercom/impression.rb
81
81
  - lib/intercom/message_thread.rb
82
- - lib/intercom/shallow_hash.rb
83
82
  - lib/intercom/social_profile.rb
84
83
  - lib/intercom/unix_timestamp_unwrapper.rb
85
84
  - lib/intercom/user.rb
85
+ - lib/intercom/user_collection_proxy.rb
86
+ - lib/intercom/user_custom_data.rb
86
87
  - lib/intercom/user_resource.rb
87
88
  - lib/intercom/version.rb
88
89
  - spec/integration/intercom_api_integration_spec.rb
89
90
  - spec/unit/intercom/impression_spec.rb
90
91
  - spec/unit/intercom/message_thread_spec.rb
92
+ - spec/unit/intercom/user_collection_proxy_spec.rb
93
+ - spec/unit/intercom/user_custom_data_spec.rb
91
94
  - spec/unit/intercom/user_resource_spec.rb
92
95
  - spec/unit/intercom/user_spec.rb
93
96
  - spec/unit/intercom_spec.rb
@@ -120,7 +123,10 @@ test_files:
120
123
  - spec/integration/intercom_api_integration_spec.rb
121
124
  - spec/unit/intercom/impression_spec.rb
122
125
  - spec/unit/intercom/message_thread_spec.rb
126
+ - spec/unit/intercom/user_collection_proxy_spec.rb
127
+ - spec/unit/intercom/user_custom_data_spec.rb
123
128
  - spec/unit/intercom/user_resource_spec.rb
124
129
  - spec/unit/intercom/user_spec.rb
125
130
  - spec/unit/intercom_spec.rb
126
131
  - spec/unit/spec_helper.rb
132
+ has_rdoc:
data/changed.txt DELETED
@@ -1,8 +0,0 @@
1
- 0.0.3
2
- - renamed Intercom.secret_key to Intercom.api_key for consistency throughout our app and docs
3
-
4
- 0.0.2
5
- - updates to reflect changes to resources served by the api
6
-
7
- 0.0.1
8
- - experimental version
@@ -1,9 +0,0 @@
1
- module Intercom
2
- # Sub-class of {Hash} which doesn't allow {Array} or {Hash} values.
3
- class ShallowHash < Hash
4
- def []=(key, value)
5
- raise ArgumentError.new("custom_data does not support nested data structures (key: #{key}, value: #{value}") if value.is_a?(Array) || value.is_a?(Hash)
6
- super(key, value)
7
- end
8
- end
9
- end