interfax 0.1.3 → 0.2.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.
data/README.markdown CHANGED
@@ -1,7 +1,12 @@
1
1
  # Interfax/Ruby
2
2
 
3
- This library is a wrapper for the interfax.net fax service. Until now only sending of html faxes and status
4
- querying is supported.
3
+ This library is a wrapper for the interfax.net fax service. For now,
4
+ you can do the following:
5
+
6
+ * Send HTML faxes
7
+ * Query the status of a sent fax
8
+ * Retrieve a list of incoming faxes
9
+ * Get the image for a sent fax
5
10
 
6
11
  If you're willing to help, just drop me a line.
7
12
 
@@ -11,6 +16,8 @@ If you're willing to help, just drop me a line.
11
16
 
12
17
  ## Usage
13
18
 
19
+ ### Outgoing Faxes
20
+
14
21
  Create a class, inherit from Interfax::Base and set authentication parameters:
15
22
 
16
23
  class OrderFax < Interfax::Base
@@ -19,7 +26,7 @@ Create a class, inherit from Interfax::Base and set authentication parameters:
19
26
  end
20
27
 
21
28
 
22
- ### Sending
29
+ #### Sending Faxes
23
30
 
24
31
  Creating a fax:
25
32
 
@@ -37,7 +44,7 @@ Finally:
37
44
 
38
45
  result_id = fax.deliver
39
46
 
40
- ### Querying
47
+ #### Getting Sent Faxes
41
48
 
42
49
  To get all faxes:
43
50
 
@@ -55,6 +62,59 @@ or get more than one at once:
55
62
 
56
63
  OrderFax.find(123456789,234567890)
57
64
 
65
+
66
+ ### Incoming Faxes
67
+
68
+
69
+ #### Getting Incoming Faxes
70
+
71
+ To get a list of incoming faxes, you can either use the base class:
72
+
73
+ Interfax::Incoming.username = 'my_interfax_username'
74
+ Interfax::Incoming.password = 'my_interfax_password'
75
+ Interfax::Incoming.limit = 15 # optional, default 100
76
+ Interfax::Incoming.mark_as_read = false # optional, default false
77
+
78
+ # fetch unread incoming faxes
79
+ faxes = Interfax::Incoming.new
80
+
81
+ If you want you can define your own subclass -- useful for keeping
82
+ authentication info handy -- you can do so:
83
+
84
+ class IncomingFaxes < Interfax::Incoming
85
+ self.username = 'my_interfax_username'
86
+ self.password = 'my_interfax_password'
87
+ end
88
+
89
+ # fetch all incoming faxes
90
+ faxes = Interfax::Incoming.all
91
+
92
+ There are four methods for fetching incoming faxes:
93
+
94
+ #all - Fetch all incoming faxes
95
+ #unread - Fetch unread incoming faxes
96
+ #account_all - Fetch incoming faxes for all users on your account
97
+ #account_unread - Fetch unread incoming faxes for all users on your account
98
+
99
+ The account_ methods require that you have admin privileges. For more
100
+ information, see the Interfax API documentation.
101
+
102
+
103
+ #### Getting Incoming Fax Images
104
+
105
+ The Interfax::Incoming methods described above return an array of
106
+ instances of the class you called it on. If you're using the
107
+ Interfax::Incoming class you'll get those. If you use a subclass,
108
+ we'll instantiate that class and return an array of those.
109
+
110
+ In either event, call the `#image` method to fetch the PDF/TIF of the
111
+ fax as a string, suitable for writing to the filesystem in the normal fashion.
112
+
113
+ # Assume you've defined a sublcass as above
114
+ all_faxes = IncomingFaxes.all
115
+ all_faxes[0].image # return the first fax's image
116
+
117
+
58
118
  ## Interfax API documentation
59
119
 
60
120
  http://www.interfax.net/en/dev/webservice/reference/methods
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0
@@ -0,0 +1,204 @@
1
+ require 'soap/wsdlDriver'
2
+ require 'Base64'
3
+
4
+ module Interfax
5
+
6
+ # = Interfax::Incoming
7
+ #
8
+ # Allows interaction with the Interfax Inbound API, documented at:
9
+ # http://www.interfax.net/en/dev/webservice/reference_in
10
+ #
11
+ # == Retrieving incoming faxes
12
+ #
13
+ # Set the +username+ and +password+ variables appropriately, and
14
+ # then call one of the query methods, which are described in further
15
+ # detail below.
16
+ #
17
+ # Interfax::Incoming.username = 'my_interfax_username'
18
+ # Interfax::Incoming.password = 'my_interfax_password'
19
+ # faxes = Interfax::Incoming.unread
20
+ #
21
+ # You can extend Interfax::Incoming, and the query methdos will
22
+ # return instances of your subclass. This is useful for extending
23
+ # functionality, or consolidating your configuration in one place.
24
+ #
25
+ # class InboundFax < Interfax::Incoming
26
+ # self.username = 'my_interfax_username'
27
+ # self.password = 'my_interfax_password'
28
+ #
29
+ # def write_image_to_file
30
+ # File.open("/path/to/file", 'w') { |f| f.write(image)
31
+ # end
32
+ # end
33
+ #
34
+ # faxes = InboundFax.all # this is an array of InboundFax objects
35
+ # faxes.map(&:write_image_to_file)
36
+ #
37
+ # == Methods
38
+ #
39
+ # The methods available for fetching or querying inbound faxes are:
40
+ #
41
+ # * +#all+ - Retrieves all inbound faxes, up to +limit+.
42
+ # * +#unread+ - Retrieves any unread faxes, up to +limit+.
43
+ # * +#account_all+ - Retrieves inbound faxes for all users on your
44
+ # account, up to +limit+. Requires an administrator account.
45
+ # * +#account_unread+ - Same as above, but fetches only unread
46
+ # faxes. Also requires an administrator account.
47
+ #
48
+ # As seen above, these methods return instances of the class they're
49
+ # called on, which makes it easy to extend your objects for custom
50
+ # functionality.
51
+ #
52
+ # == Configuration
53
+ #
54
+ # We require that the following two configuration variables be set
55
+ # at the class level, as seen in the examples above.
56
+ #
57
+ # * +username+ - [required] Your interfax username.
58
+ # * +password+ - [requierd] Your interfax password.
59
+ #
60
+ # We also support a few other configuration methods, which are
61
+ # explained further in Interfax's API documentation.
62
+ #
63
+ # * +limit+ - [1..100, default 100] The maximum number of faxes to return.
64
+ # * +mark_as_read+ - [boolean, default false] Whether or not to mark
65
+ # retrieved faxes as sent.
66
+ #
67
+ # These options are also accepted as options when requesting faxes:
68
+ #
69
+ # # Assume username/password already set
70
+ # Interfax::Incoming.all(:limit => 10, :mark_as_read => true)
71
+ #
72
+ # Options supplied this way will temporarily override any options
73
+ # set on the class.
74
+
75
+ class Incoming
76
+
77
+ class << self
78
+ attr_accessor :username, :password, :mark_as_read, :limit #:nodoc:
79
+
80
+ def soap_client #:nodoc:
81
+ SOAP::WSDLDriverFactory.
82
+ new("https://ws.interfax.net/inbound.asmx?WSDL").
83
+ create_rpc_driver
84
+ end
85
+
86
+ def query(type, opts = {}) #:nodoc:
87
+ result = self.soap_client.GetList(:Username => self.username,
88
+ :Password => self.password,
89
+ :MaxItems => opts[:limit] || self.limit || 100,
90
+ :MarkAsRead => opts[:mark_as_read] || self.mark_as_read || false,
91
+ :LType => type
92
+ )
93
+
94
+ return [] if result.nil? || !defined?(result.objMessageItem)
95
+ [*result.objMessageItem.messageItem].map do |fax|
96
+ self.new(fax.messageID, fax.messageSize)
97
+ end
98
+ end
99
+
100
+ # Returns all inbound messages for your user up to the +limit+
101
+ # option. Optionally marks the faxes as read.
102
+ #
103
+ # ==== Options (as hash)
104
+ #
105
+ # Both of these will default to whatever values you've set for
106
+ # the class, or 100 (limit) or false (mark_as_read) if you haven't.
107
+ # * +:limit+ - Maximum number of faxes to return.
108
+ # * +:mark_as_read+: Mark fetched faxes as read.
109
+
110
+ def all(opts = {})
111
+ query('AllMessages', opts)
112
+ end
113
+
114
+ # Returns any unread messages for your user up to the +limit+
115
+ # option. Optionally marks the faxes as read.
116
+ #
117
+ # ==== Options (as hash)
118
+ #
119
+ # Both of these will default to whatever values you've set for
120
+ # the class, or 100 (limit) or false (mark_as_read) if you haven't.
121
+ # * +:limit+ - Maximum number of faxes to return.
122
+ # * +:mark_as_read+: Mark fetched faxes as read.
123
+
124
+ def unread(opts = {})
125
+ query('NewMessages', opts)
126
+ end
127
+
128
+ # Returns all messages for all users on your account, up
129
+ # to the +limit+ option. Optionally marks the faxes as
130
+ # read. Requires your user be an administrator for your account.
131
+ #
132
+ # ==== Options (as hash)
133
+ #
134
+ # Both of these will default to whatever values you've set for
135
+ # the class, or 100 (limit) or false (mark_as_read) if you haven't.
136
+ # * +:limit+ - Maximum number of faxes to return.
137
+ # * +:mark_as_read+: Mark fetched faxes as read.
138
+
139
+ def account_all(opts = {})
140
+ query('AccountAllMessages', opts)
141
+ end
142
+
143
+ # Returns any unread messages for all users on your account, up
144
+ # to the +limit+ option. Optionally marks the faxes as
145
+ # read. Requires your user be an administrator for your account.
146
+ #
147
+ # ==== Options (as hash)
148
+ #
149
+ # Both of these will default to whatever values you've set for
150
+ # the class, or 100 (limit) or false (mark_as_read) if you haven't.
151
+ # * +:limit+ - Maximum number of faxes to return.
152
+ # * +:mark_as_read+: Mark fetched faxes as read.
153
+
154
+ def account_unread(opts = {})
155
+ query('AccountNewMessages', opts)
156
+ end
157
+
158
+ end
159
+
160
+
161
+
162
+ attr_accessor :username, :password, :mark_as_read, :chunk_size, :message_id, :message_size, :image #:nodoc:
163
+
164
+ def initialize(message_id, message_size)
165
+ @username = self.class.username
166
+ @password = self.class.password
167
+ @mark_as_read = self.class.mark_as_read || false
168
+ @chunk_size = 100000
169
+ @message_id = message_id
170
+ @message_size = message_size.to_i
171
+ @image = nil
172
+ end
173
+
174
+ # Retrieves the image from the Interfax Inbound API, as a
175
+ # string. Suitable for writing to a file or streaming to a client.
176
+ def image
177
+ @image || fetch_image
178
+ end
179
+
180
+ def fetch_image #:nodoc:
181
+ @image = ""
182
+ downloaded_size = 0
183
+ while downloaded_size < @message_size
184
+ result = self.class.soap_client.GetImageChunk(:Username => @username,
185
+ :Password => @password,
186
+ :MessageID => @message_id,
187
+ :MarkAsRead => @mark_as_read,
188
+ :ChunkSize => @chunk_size,
189
+ :From => downloaded_size)
190
+
191
+ # TODO: Make this throw a nicer exception on failure
192
+ if defined?(result.image)
193
+ @image << Base64.decode64(result.image)
194
+ end
195
+ downloaded_size += @chunk_size
196
+
197
+ end
198
+
199
+ @image
200
+ end
201
+
202
+ end
203
+
204
+ end
data/lib/interfax.rb CHANGED
@@ -5,4 +5,5 @@ $:.unshift(File.dirname(__FILE__)) unless
5
5
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
6
6
 
7
7
  require 'interfax/fax_item'
8
- require 'interfax/base'
8
+ require 'interfax/base'
9
+ require 'interfax/incoming'
@@ -0,0 +1,11 @@
1
+ module IncomingHelper
2
+
3
+ class MockInterfaxResponse < Struct.new(:objMessageItem); end
4
+ class ObjMessageItem < Struct.new(:messageItem); end
5
+ class MessageItem < Struct.new(:messageID, :messageSize); end
6
+
7
+
8
+ def mock_getlist_response(count = 1)
9
+ MockInterfaxResponse.new(ObjMessageItem.new([MessageItem.new('messageid', '200000')] * count))
10
+ end
11
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ include IncomingHelper
3
+
4
+ describe Interfax::Incoming do
5
+
6
+ before(:each) do
7
+ @client_mock = mock("SoapClient",
8
+ :GetList => true,
9
+ :GetImageChunk => true)
10
+
11
+ Interfax::Incoming.stub!(:soap_client).and_return(@client_mock)
12
+ end
13
+
14
+ context "querying for incoming faxes" do
15
+
16
+ it "should query for all messages" do
17
+ Interfax::Incoming.should_receive(:query).with('AllMessages', anything())
18
+ Interfax::Incoming.all
19
+ end
20
+
21
+ it "should query for unread messages" do
22
+ Interfax::Incoming.should_receive(:query).with('NewMessages', anything())
23
+ Interfax::Incoming.unread
24
+ end
25
+
26
+ it "should query for all users' unread messages" do
27
+ Interfax::Incoming.should_receive(:query).with('AccountNewMessages', anything())
28
+ Interfax::Incoming.account_unread
29
+ end
30
+
31
+ it "should query for all users' messages" do
32
+ Interfax::Incoming.should_receive(:query).with('AccountAllMessages', anything())
33
+ Interfax::Incoming.account_all
34
+ end
35
+
36
+ it "should respect the :limit argument over a class-defined limit" do
37
+ @client_mock.should_receive(:GetList).with(hash_including(:MaxItems => 24))
38
+ Interfax::Incoming.limit = 50
39
+ Interfax::Incoming.all(:limit => 24)
40
+ end
41
+
42
+ it "should use the class-specified limit over the default" do
43
+ @client_mock.should_receive(:GetList).with(hash_including(:MaxItems => 50))
44
+ Interfax::Incoming.limit = 50
45
+ Interfax::Incoming.all
46
+ end
47
+
48
+ it "should respect the :mark_as_read argument over the class-defined value" do
49
+ @client_mock.should_receive(:GetList).with(hash_including(:MarkAsRead => true))
50
+ Interfax::Incoming.mark_as_read = false
51
+ Interfax::Incoming.all(:mark_as_read => true)
52
+ end
53
+
54
+ it "should use the class-specified mark_as_read over the default" do
55
+ @client_mock.should_receive(:GetList).with(hash_including(:MarkAsRead => true))
56
+ Interfax::Incoming.mark_as_read = true
57
+ Interfax::Incoming.all
58
+ end
59
+
60
+
61
+ it "should return instances of the base class when called on the base class" do
62
+ @client_mock.stub!(:GetList).and_return(mock_getlist_response)
63
+ Interfax::Incoming.all[0].class.should == Interfax::Incoming
64
+ end
65
+
66
+ it "should return instances of the subclass when called on a subclass" do
67
+ class TestFax < Interfax::Incoming; end
68
+
69
+ @client_mock.
70
+ stub!(:GetList).
71
+ and_return(mock_getlist_response)
72
+ TestFax.stub!(:soap_client).and_return(@client_mock)
73
+
74
+ TestFax.all[0].class.should == TestFax
75
+ end
76
+
77
+ end
78
+
79
+ context "fetching images" do
80
+
81
+ before(:each) do
82
+ @fax = Interfax::Incoming.new('message_id', '200000')
83
+ end
84
+
85
+ it "should fetch the image when asked" do
86
+ @client_mock.should_receive(:GetImageChunk).at_least(:once)
87
+ @fax.image
88
+ end
89
+
90
+ it "should cache the image once received" do
91
+ @client_mock.should_receive(:GetImageChunk).at_least(:once)
92
+ @fax.image
93
+ @client_mock.should_not_receive(:GetImageChunk)
94
+ @fax.image
95
+ end
96
+
97
+ it "should respect the class-defined mark_as_read option" do
98
+ Interfax::Incoming.mark_as_read = true
99
+ @client_mock.should_receive(:GetImageChunk).with(hash_including(:MarkAsRead => true))
100
+ @fax.image
101
+ end
102
+
103
+
104
+ end
105
+
106
+ end
107
+
@@ -2,6 +2,6 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "Interfax" do
4
4
  it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
5
+ pending "hey buddy, you should probably rename this file and start specing for real"
6
6
  end
7
7
  end
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
  require 'interfax'
4
4
  require 'spec'
5
5
  require 'spec/autorun'
6
+ require 'incoming_helper'
6
7
 
7
8
  Spec::Runner.configure do |config|
8
9
 
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: interfax
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
- - 1
9
- - 3
10
- version: 0.1.3
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Sascha Brink
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2011-01-20 00:00:00 +01:00
17
+ date: 2011-04-28 00:00:00 +02:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 13
30
28
  segments:
31
29
  - 1
32
30
  - 2
@@ -51,6 +49,9 @@ files:
51
49
  - lib/interfax.rb
52
50
  - lib/interfax/base.rb
53
51
  - lib/interfax/fax_item.rb
52
+ - lib/interfax/incoming.rb
53
+ - spec/incoming_helper.rb
54
+ - spec/incoming_spec.rb
54
55
  - spec/interfax_spec.rb
55
56
  - spec/spec.opts
56
57
  - spec/spec_helper.rb
@@ -68,7 +69,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
69
  requirements:
69
70
  - - ">="
70
71
  - !ruby/object:Gem::Version
71
- hash: 3
72
72
  segments:
73
73
  - 0
74
74
  version: "0"
@@ -77,7 +77,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
77
  requirements:
78
78
  - - ">="
79
79
  - !ruby/object:Gem::Version
80
- hash: 3
81
80
  segments:
82
81
  - 0
83
82
  version: "0"
@@ -89,5 +88,7 @@ signing_key:
89
88
  specification_version: 3
90
89
  summary: A ruby wrapper for the interfax webservice (SOAP)
91
90
  test_files:
91
+ - spec/incoming_helper.rb
92
+ - spec/incoming_spec.rb
92
93
  - spec/interfax_spec.rb
93
94
  - spec/spec_helper.rb