intercom 0.0.4 → 0.0.5

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