contextio 0.5.0 → 1.0.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.
Files changed (65) hide show
  1. data/.document +4 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -0
  5. data/ChangeLog.md +5 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE.md +20 -0
  8. data/README.md +62 -22
  9. data/Rakefile +46 -36
  10. data/contextio.gemspec +30 -0
  11. data/lib/contextio.rb +69 -583
  12. data/lib/contextio/account.rb +132 -0
  13. data/lib/contextio/account_collection.rb +57 -0
  14. data/lib/contextio/account_sync_data.rb +22 -0
  15. data/lib/contextio/api.rb +162 -0
  16. data/lib/contextio/api/association_helpers.rb +17 -0
  17. data/lib/contextio/api/resource.rb +230 -0
  18. data/lib/contextio/api/resource_collection.rb +174 -0
  19. data/lib/contextio/api/url_builder.rb +153 -0
  20. data/lib/contextio/body_part.rb +45 -0
  21. data/lib/contextio/body_part_collection.rb +13 -0
  22. data/lib/contextio/connect_token.rb +57 -0
  23. data/lib/contextio/connect_token_collection.rb +44 -0
  24. data/lib/contextio/contact.rb +43 -0
  25. data/lib/contextio/contact_collection.rb +21 -0
  26. data/lib/contextio/email_address.rb +53 -0
  27. data/lib/contextio/email_address_collection.rb +21 -0
  28. data/lib/contextio/email_settings.rb +146 -0
  29. data/lib/contextio/file.rb +92 -0
  30. data/lib/contextio/file_collection.rb +13 -0
  31. data/lib/contextio/folder.rb +56 -0
  32. data/lib/contextio/folder_collection.rb +18 -0
  33. data/lib/contextio/folder_sync_data.rb +32 -0
  34. data/lib/contextio/message.rb +96 -0
  35. data/lib/contextio/message_collection.rb +35 -0
  36. data/lib/contextio/oauth_provider.rb +29 -0
  37. data/lib/contextio/oauth_provider_collection.rb +46 -0
  38. data/lib/contextio/source.rb +55 -0
  39. data/lib/contextio/source_collection.rb +41 -0
  40. data/lib/contextio/source_sync_data.rb +23 -0
  41. data/lib/contextio/thread.rb +15 -0
  42. data/lib/contextio/thread_collection.rb +25 -0
  43. data/lib/contextio/version.rb +11 -0
  44. data/lib/contextio/webhook.rb +39 -0
  45. data/lib/contextio/webhook_collection.rb +26 -0
  46. data/spec/config.yml.example +3 -0
  47. data/spec/contextio/account_collection_spec.rb +78 -0
  48. data/spec/contextio/account_spec.rb +52 -0
  49. data/spec/contextio/api/association_helpers_spec.rb +28 -0
  50. data/spec/contextio/api/resource_collection_spec.rb +286 -0
  51. data/spec/contextio/api/resource_spec.rb +467 -0
  52. data/spec/contextio/api/url_builder_spec.rb +78 -0
  53. data/spec/contextio/api_spec.rb +123 -0
  54. data/spec/contextio/connect_token_collection_spec.rb +74 -0
  55. data/spec/contextio/connect_token_spec.rb +58 -0
  56. data/spec/contextio/email_settings_spec.rb +112 -0
  57. data/spec/contextio/oauth_provider_collection_spec.rb +36 -0
  58. data/spec/contextio/oauth_provider_spec.rb +120 -0
  59. data/spec/contextio/source_collection_spec.rb +57 -0
  60. data/spec/contextio/source_spec.rb +52 -0
  61. data/spec/contextio/version_spec.rb +10 -0
  62. data/spec/contextio_spec.rb +64 -0
  63. data/spec/spec_helper.rb +17 -0
  64. metadata +234 -12
  65. data/README.textile +0 -29
@@ -0,0 +1,43 @@
1
+ require 'contextio/api/resource'
2
+
3
+ class ContextIO
4
+ class Contact
5
+ include ContextIO::API::Resource
6
+
7
+ self.primary_key = :email
8
+ self.association_name = :contact
9
+
10
+ belongs_to :account
11
+
12
+ lazy_attributes :emails, :name, :thumbnail, :last_received, :last_sent,
13
+ :count
14
+ private :last_received, :last_sent
15
+
16
+ def email
17
+ @email ||= emails.first
18
+ end
19
+
20
+ def last_received_at
21
+ last_received ? Time.at(last_received) : nil
22
+ end
23
+
24
+ def last_sent_at
25
+ last_sent ? Time.at(last_sent) : nil
26
+ end
27
+
28
+ # Poor man's has_many
29
+ def threads
30
+ account.threads.where(email: email)
31
+ end
32
+
33
+ # Poor man's has_many
34
+ def messages
35
+ account.messages.where(email: email)
36
+ end
37
+
38
+ # Poor man's has_many
39
+ def files
40
+ account.files.where(email: email)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'api/resource_collection'
2
+ require_relative 'contact'
3
+
4
+ class ContextIO
5
+ class ContactCollection
6
+ include ContextIO::API::ResourceCollection
7
+
8
+ self.resource_class = ContextIO::Contact
9
+ self.association_name = :contacts
10
+
11
+ belongs_to :account
12
+
13
+ private
14
+
15
+ # The request that comes back is not formatted the same as other
16
+ # collections, so we need to override it and pick out the right key.
17
+ def attribute_hashes
18
+ @attribute_hashes ||= api.request(:get, resource_url, where_constraints)['matches']
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,53 @@
1
+ require 'contextio/api/association_helpers'
2
+
3
+ class ContextIO
4
+ class EmailAddress
5
+ def self.association_name
6
+ :email_address
7
+ end
8
+ ContextIO::API::AssociationHelpers.register_resource(self, :email_address)
9
+
10
+ # (see ContextIO#api)
11
+ attr_reader :api, :account, :email, :validated, :primary
12
+ private :validated, :primary
13
+ #
14
+ # @private
15
+ #
16
+ # For internal use only. Users of this gem shouldn't be calling this
17
+ # directly.
18
+ #
19
+ # @param [API] api A handle on the Context.IO API.
20
+ # @param [Hash{String, Symbol => String, Numeric, Boolean}] options A Hash
21
+ # of attributes describing the resource.
22
+ def initialize(api, options = {})
23
+ @api = api
24
+ @account = options.delete(:account) || options.delete('account')
25
+
26
+ options.each do |key, value|
27
+ instance_variable_set("@#{key}", value)
28
+
29
+ unless self.respond_to?(key)
30
+ define_singleton_method(key) do
31
+ instance_variable_get("@#{key}")
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def validated?
38
+ !!validated
39
+ end
40
+
41
+ def primary?
42
+ !!primary
43
+ end
44
+
45
+ def set_primary
46
+ api.request(:post, resource_url, primary: 1)['success']
47
+ end
48
+
49
+ def delete
50
+ api.request(:delete, resource_url)['success']
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'api/resource_collection'
2
+ require_relative 'email_address'
3
+
4
+ class ContextIO
5
+ class EmailAddressCollection
6
+ include ContextIO::API::ResourceCollection
7
+
8
+ self.resource_class = ContextIO::EmailAddress
9
+ self.association_name = :email_addresses
10
+
11
+ belongs_to :account
12
+
13
+ def create(address)
14
+ result_hash = api.request(:post, resource_url, email_address: address)
15
+
16
+ result_hash.delete('success')
17
+
18
+ resource_class.new(api, result_hash)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,146 @@
1
+ class ContextIO
2
+ # Represents the IMAP settings for a given email address. The API
3
+ # documentation refers to this as 'discovery'. This process is hit-and-miss.
4
+ # If noting is found, many attributes are likely to be nil and blank. Check
5
+ # {EmailSettings#found? EmailSettings#found?}.
6
+ class EmailSettings
7
+ # (see ContextIO#api)
8
+ attr_reader :api
9
+
10
+ # @!attribute [r] type
11
+ # @return [String] The type og the provider (like 'gmail'). Will fetch from
12
+ # the API if necessary.
13
+ def type
14
+ return @type if instance_variable_defined?(:@type)
15
+
16
+ fetch_attributes
17
+
18
+ @type
19
+ end
20
+
21
+ # @!attribute [r] documentation
22
+ # @return [Array<String>] A list of documentation pages that pertain to the
23
+ # provider. Will fetch from the API if necessary.
24
+ def documentation
25
+ return @documentation if instance_variable_defined?(:@documentation)
26
+
27
+ fetch_attributes
28
+
29
+ @documentation
30
+ end
31
+
32
+ # @!attribute [r] email
33
+ # @return [String] The email address associated with these IMAP settings.
34
+ attr_reader :email, :source_type
35
+
36
+ # Not sure why, but the below comment has to be below the attr_reader call
37
+ # above.
38
+
39
+ # @!attribute [r] source_type
40
+ # @return [String] The only source type currently supported by the API is
41
+ # 'IMAP'.
42
+
43
+ # (see ContextIO::OAuthProviderCollection#initialize)
44
+ # @param [String] email The email address to fetch the settings for.
45
+ # @param [String] source_type The only source type currently supported by
46
+ # the API is 'IMAP'.
47
+ def initialize(api, email, source_type = 'IMAP')
48
+ @api = api
49
+ @email = email
50
+ @source_type = source_type
51
+ end
52
+
53
+ # @!attribute [r] resource_url
54
+ # @return [String] The path for discovering email settings.
55
+ def resource_url
56
+ 'discovery'
57
+ end
58
+
59
+ # @!attribute [r] found?
60
+ # @return [Boolean] Whether the settings were able to be fetched. Will fetch
61
+ # from the API if necessary.
62
+ def found?
63
+ found
64
+ end
65
+
66
+ # @!attribute [r] server
67
+ # @return [String] FQDN of the IMAP server. Will fetch from the API if
68
+ # necessary.
69
+ def server
70
+ imap['server']
71
+ end
72
+
73
+ # @!attribute [r] username
74
+ # @return [String] The username for authentication purposes. Will fetch
75
+ # from the API if necessary.
76
+ def username
77
+ imap['username']
78
+ end
79
+
80
+ # @!attribute [r] port
81
+ # @return [Integer] The network port the IMAP server is listening on. Will
82
+ # fetch from the API if necessary.
83
+ def port
84
+ imap['port']
85
+ end
86
+
87
+ # @!attribute [r] oauth?
88
+ # @return [Boolean] Whether the IMAP server supports OAuth or not. Will
89
+ # fetch from the API if necessary.
90
+ def oauth?
91
+ !!imap['oauth']
92
+ end
93
+
94
+ # @!attribute [r] use_ssl?
95
+ # @return [Boolean] Whether the IMAP server uses SSL for connections. Will
96
+ # fetch from the API if necessary.
97
+ def use_ssl?
98
+ !!imap['use_ssl']
99
+ end
100
+
101
+ private
102
+
103
+ # @!attribute [r] imap
104
+ # @return [Hash{String => String, Boolean, Integer}] Attributes of the IMAP
105
+ # server in question.
106
+ def imap
107
+ return @imap if instance_variable_defined?(:@imap)
108
+
109
+ fetch_attributes
110
+
111
+ @imap
112
+ end
113
+
114
+ # @!attribute [r] found
115
+ # @return [Boolean] Whether the settings were able to be fetched. Will fetch
116
+ # from the API if necessary.
117
+ def found
118
+ return @found if instance_variable_defined?(:@found)
119
+
120
+ fetch_attributes
121
+
122
+ @found
123
+ end
124
+
125
+ # Fetches attributes from the API for the email settings.
126
+ #
127
+ # Defines getter methods for any attributes that come back and don't
128
+ # already have them. This way, if the API expands, the gem will still let
129
+ # users get attributes we didn't explicitly declare as lazy.
130
+ def fetch_attributes
131
+ attr_hashes = api.request(:get, resource_url, 'email' => email, 'source_type' => source_type)
132
+
133
+ attr_hashes.each do |key, value|
134
+ instance_variable_set("@#{key}", value)
135
+
136
+ unless respond_to?(key)
137
+ instance_eval <<-RUBY
138
+ def #{key}
139
+ @#{key}
140
+ end
141
+ RUBY
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,92 @@
1
+ require 'contextio/api/resource'
2
+ require 'contextio/source_sync_data'
3
+
4
+ class ContextIO
5
+ class File
6
+ include ContextIO::API::Resource
7
+
8
+ self.primary_key = :file_id
9
+ self.association_name = :file
10
+
11
+ belongs_to :account
12
+
13
+ lazy_attributes :size, :type, :subject, :date, :addresses, :personInfo,
14
+ :email_message_id, :message_id, :date_indexed,
15
+ :date_received, :file_name, :file_name_structure,
16
+ :body_section, :content_disposition, :file_id,
17
+ :is_tnef_part, :gmail_message_id, :gmail_thread_id,
18
+ :supports_preview, :is_embedded
19
+ private :date, :addresses, :message_id, :date_indexed, :date_received,
20
+ :is_tnef_part, :is_embedded, :personInfo
21
+
22
+ def received_at
23
+ @received_at ||= Time.at(date_received)
24
+ end
25
+
26
+ def indexed_at
27
+ @indexed_at ||= Time.at(indexed_at)
28
+ end
29
+
30
+ def to
31
+ addresses['to']
32
+ end
33
+
34
+ def from
35
+ addresses['from']
36
+ end
37
+
38
+ def person_info
39
+ personInfo
40
+ end
41
+
42
+ def tnef_part?
43
+ !!is_tnef_part
44
+ end
45
+
46
+ def embedded?
47
+ !!is_embedded
48
+ end
49
+
50
+ def content
51
+ @content ||= api.request(:get, "#{resource_url}/content")
52
+ end
53
+
54
+ def content_link
55
+ @content_link ||= api.raw_request(:get, "#{resource_url}/content", as_link: 1)
56
+ end
57
+
58
+ def related_files
59
+ return @related_files if @related_files
60
+
61
+ attribute_hashes = api.request(:get, "#{resource_url}/related")
62
+
63
+ @related_files = FileCollection.new(api, attribute_hashes: attribute_hashes, account: account)
64
+
65
+ return @related_files
66
+ end
67
+
68
+ def revisions
69
+ return @revisions if @revisions
70
+
71
+ attribute_hashes = api.request(:get, "#{resource_url}/revisions")
72
+
73
+ @revisions = FileCollection.new(api, attribute_hashes: attribute_hashes, account: account)
74
+
75
+ return @revisions
76
+ end
77
+
78
+ def sync_data
79
+ return @sync_data if @sync_data
80
+
81
+ sync_hashes = api.request(:get, "#{resource_url}/sync")
82
+
83
+ @sync_data = ContextIO::SourceSyncData.new(sync_hashes)
84
+
85
+ return @sync_data
86
+ end
87
+
88
+ def sync!
89
+ api.request(:post, "#{resource_url}/sync")['success']
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'api/resource_collection'
2
+ require_relative 'file'
3
+
4
+ class ContextIO
5
+ class FileCollection
6
+ include ContextIO::API::ResourceCollection
7
+
8
+ self.resource_class = ContextIO::File
9
+ self.association_name = :files
10
+
11
+ belongs_to :account
12
+ end
13
+ end
@@ -0,0 +1,56 @@
1
+ require 'contextio/api/association_helpers'
2
+
3
+ class ContextIO
4
+ class Folder
5
+ def self.association_name
6
+ :folder
7
+ end
8
+ ContextIO::API::AssociationHelpers.register_resource(self, :folder)
9
+
10
+ # (see ContextIO#api)
11
+ attr_reader :api, :source, :name, :attributes, :delim, :nb_messages,
12
+ :uidvalidity, :nb_unseen_messages
13
+ private :attributes
14
+
15
+ # @private
16
+ #
17
+ # For internal use only. Users of this gem shouldn't be calling this
18
+ # directly.
19
+ #
20
+ # @param [API] api A handle on the Context.IO API.
21
+ # @param [Hash{String, Symbol => String, Numeric, Boolean}] options A Hash
22
+ # of attributes describing the resource.
23
+ def initialize(api, options = {})
24
+ @api = api
25
+ @source = options.delete(:source) || options.delete('source')
26
+
27
+ options.each do |key, value|
28
+ instance_variable_set("@#{key}", value)
29
+
30
+ unless self.respond_to?(key)
31
+ define_singleton_method(key) do
32
+ instance_variable_get("@#{key}")
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def has_children?
39
+ attributes['HasChildren']
40
+ end
41
+
42
+ def marked?
43
+ attributes['Marked']
44
+ end
45
+
46
+ def imap_attributes
47
+ attributes
48
+ end
49
+
50
+ def messages
51
+ association_class = ContextIO::API::AssociationHelpers.class_for_association_name(:messages)
52
+
53
+ @messages ||= association_class.new(api, folder: self)
54
+ end
55
+ end
56
+ end