contextio 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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