rexchange 0.2.0 → 0.3.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/CHANGELOG CHANGED
@@ -1,8 +1,19 @@
1
+ -- 0.3.0:
2
+ * Added support for Appointments
3
+ * Options hash for Session creation is gone. The new form is: "RExchange::Session.new(url, username, password)"
4
+ * Removed GenericItem#[], GenericItem#method_missing (attributes are explicit now)
5
+ * Removed Folder#get_messages, #get_contacts and #messages_in. Folders are now Enumerables using Enumerable#entries, #each, etc
6
+ to retrieve entries according to the folder's content-class. So a 'mailfolder' will retrieve Message instances,
7
+ a 'calendarfolder' will retrieve Appointment instances, and so on.
8
+
1
9
  -- 0.2.0:
2
10
  * Fixed a bug in Message#move_to when passing folders
3
11
  * Added support for Contact browsing
4
12
  * Added GenericItem to abstract Contacts and Messages
5
13
  * Renamed Folder#messages to Folder#get_messages to avoid naming collisions
14
+ * Modified attributes in GenericItem that end with "date" to be Time::parse'd
15
+ * Added "r_exchange.rb" for Rails auto-require compatibility
16
+ * Now calling String#normalize on attribute names instead of just String#tr('-', '_')
6
17
 
7
18
  -- 0.1.4:
8
19
  * Moved Folder::normalize_folder_name to String#normalize, and added support for MixedCase sources.
data/RAKEFILE CHANGED
@@ -2,12 +2,13 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'rake'
5
+ require 'rake/testtask'
5
6
  require 'rake/rdoctask'
6
7
  require 'rake/gempackagetask'
7
8
  require 'rake/contrib/rubyforgepublisher'
8
9
  require 'pscp'
9
10
 
10
- PACKAGE_VERSION = '0.2.0'
11
+ PACKAGE_VERSION = '0.3.0'
11
12
 
12
13
  PACKAGE_FILES = FileList[
13
14
  'README',
@@ -64,4 +65,10 @@ end
64
65
  desc "Publish RDOC to RubyForge"
65
66
  task :rubyforge => [:rdoc, :gem] do
66
67
  Rake::SshDirPublisher.new(ENV['RUBYFORGE_USER'], ENV['RUBYFORGE_PROJECT'], 'doc').upload
68
+ end
69
+
70
+ Rake::TestTask.new do |t|
71
+ t.libs << "test"
72
+ t.test_files = FileList['test/*.rb']
73
+ t.verbose = true
67
74
  end
data/README CHANGED
@@ -4,7 +4,7 @@ RExchange is a pure ruby wrapper for the Microsoft Exchange Server WebDAV API
4
4
 
5
5
  == Things you should know
6
6
 
7
- * Requires Ruby 1.8.4 (for the extended WebDAV support in the net/http library)
7
+ * Requires Ruby 1.8.4 (or later, for the extended WebDAV support in the net/http library)
8
8
  * RExchange is cross-platform compatible, being written in pure Ruby
9
9
  * Kiwi fruits are packed with vitamins
10
10
 
@@ -36,7 +36,7 @@ RExchange is a pure ruby wrapper for the Microsoft Exchange Server WebDAV API
36
36
 
37
37
  # The RExchange::Message#move_to method moves the message to another folder, in this
38
38
  # case, an "archive" folder off of the inbox.
39
- message.move_to '/inbox/archive'
39
+ message.move_to mailbox.inbox.archive # or you could pass the string: '/inbox/archive'
40
40
  end
41
41
 
42
42
  # You can also call the RExchange::Folder#messages method if you find calling "each"
@@ -19,8 +19,8 @@ module RExchange
19
19
  DEBUG_STREAM = $log
20
20
 
21
21
  # A shortcut to RExchange::Session#new's block syntax
22
- def self.open(uri, options = {})
23
- session = RExchange::Session.new(uri, options)
22
+ def self.open(uri, username = nil, password = nil)
23
+ session = RExchange::Session.new(uri, username, password)
24
24
 
25
25
  yield session if block_given?
26
26
  return session
@@ -0,0 +1,38 @@
1
+ require 'rexchange/generic_item'
2
+
3
+ module RExchange
4
+ class Appointment < GenericItem
5
+
6
+ set_folder_type 'calendar'
7
+
8
+ attribute_mappings :all_day_event => 'urn:schemas:calendar:alldayevent',
9
+ :busy_status => 'urn:schemas:calendar:busystatus',
10
+ :contact => 'urn:schemas:calendar:contact',
11
+ :contact_url => 'urn:schemas:calendar:contacturl',
12
+ :created_on => 'urn:schemas:calendar:created',
13
+ :description_url => 'urn:schemas:calendar:descriptionurl',
14
+ :end_at => 'urn:schemas:calendar:dtend',
15
+ :created_at => 'urn:schemas:calendar:dtstamp',
16
+ :start_at => 'urn:schemas:calendar:dtstart',
17
+ :duration => 'urn:schemas:calendar:duration',
18
+ :expires_on => 'urn:schemas:calendar:exdate',
19
+ :expiry_rule => 'urn:schemas:calendar:exrule',
20
+ :has_attachment? => 'urn:schemas:httpmail:hasattachment',
21
+ :html => 'urn:schemas:httpmail:htmldescription',
22
+ :modified_on => 'urn:schemas:calendar:lastmodified',
23
+ :location => 'urn:schemas:calendar:location',
24
+ :location_url => 'urn:schemas:calendar:locationurl',
25
+ :meeting_status => 'urn:schemas:calendar:meetingstatus',
26
+ :normalized_subject => 'urn:schemas:httpmail:normalizedsubject',
27
+ :priority => 'urn:schemas:httpmail:priority',
28
+ :recurres_on => 'urn:schemas:calendar:rdate',
29
+ :reminder_offset => 'urn:schemas:calendar:reminderoffset',
30
+ :reply_time => 'urn:schemas:calendar:replytime',
31
+ :sequence => 'urn:schemas:calendar:sequence',
32
+ :subject => 'urn:schemas:httpmail:subject',
33
+ :body => 'urn:schemas:httpmail:textdescription',
34
+ :timezone => 'urn:schemas:calendar:timezone',
35
+ :uid => 'urn:schemas:calendar:uid'
36
+
37
+ end
38
+ end
@@ -3,43 +3,26 @@ require 'rexchange/generic_item'
3
3
  module RExchange
4
4
  class Contact < GenericItem
5
5
 
6
- attribute_mappings :first_name => 'given_name',
7
- :middle_name => 'middlename',
8
- :last_name => 'sn',
9
- :created_at => 'creationdate',
10
- :address => 'mailingstreet',
11
- :city => 'mailingcity',
12
- :state => 'st',
13
- :zip_code => 'mailingpostalcode',
14
- :country => 'co',
15
- :phone => 'home_phone',
16
- :business_phone => 'telephone_number',
17
- :fax => 'facsimiletelephonenumber',
18
- :email => 'email1',
19
- :website => 'businesshomepage',
20
- :company => 'o'
21
-
22
- def self.query(path)
23
- <<-QBODY
24
- <?xml version="1.0"?>
25
- <D:searchrequest xmlns:D = "DAV:">
26
- <D:sql>
27
- SELECT "urn:schemas:contacts:givenName", "urn:schemas:contacts:middlename",
28
- "urn:schemas:contacts:sn", "urn:schemas:contacts:title",
29
- "urn:schemas:contacts:mailingstreet", "urn:schemas:contacts:mailingcity",
30
- "urn:schemas:contacts:st", "urn:schemas:contacts:mailingpostalcode",
31
- "urn:schemas:contacts:co", "urn:schemas:contacts:homePhone",
32
- "urn:schemas:contacts:telephoneNumber", "urn:schemas:contacts:facsimiletelephonenumber",
33
- "urn:schemas:contacts:mobile", "urn:schemas:contacts:email1",
34
- "urn:schemas:contacts:businesshomepage", "urn:schemas:contacts:o", "DAV:creationdate"
35
- FROM SCOPE('shallow traversal of "#{path}"')
36
- WHERE "DAV:ishidden" = false
37
- AND "DAV:isfolder" = false
38
- AND "DAV:contentclass" = 'urn:content-classes:person'
39
- </D:sql>
40
- </D:searchrequest>
41
- QBODY
42
- end
6
+ set_folder_type 'contact'
7
+ set_content_class 'person'
8
+
9
+ attribute_mappings :first_name => 'urn:schemas:contacts:givenName',
10
+ :middle_name => 'urn:schemas:contacts:middlename',
11
+ :last_name => 'urn:schemas:contacts:sn',
12
+ :title => 'urn:schemas:contacts:title',
13
+ :created_at => 'DAV:creationdate',
14
+ :address => 'urn:schemas:contacts:mailingstreet',
15
+ :city => 'urn:schemas:contacts:mailingcity',
16
+ :state => 'urn:schemas:contacts:st',
17
+ :zip_code => 'urn:schemas:contacts:mailingpostalcode',
18
+ :country => 'urn:schemas:contacts:co',
19
+ :phone => 'urn:schemas:contacts:homePhone',
20
+ :business_phone => 'urn:schemas:contacts:telephoneNumber',
21
+ :fax => 'urn:schemas:contacts:facsimiletelephonenumber',
22
+ :mobile => 'urn:schemas:contacts:mobile',
23
+ :email => 'urn:schemas:contacts:email1',
24
+ :website => 'urn:schemas:contacts:businesshomepage',
25
+ :company => 'urn:schemas:contacts:o'
43
26
 
44
- end
27
+ end
45
28
  end
@@ -8,11 +8,11 @@ module RExchange
8
8
  attr_reader :user, :password, :uri
9
9
 
10
10
  # You must pass a uri, and an options hash containing :user and :password
11
- def initialize(uri, options = {})
11
+ def initialize(uri, username = nil, password = nil)
12
12
  @uri = URI.parse(uri)
13
13
  @use_ssl = (@uri.scheme.downcase == 'https')
14
- @user = @uri.userinfo ? @user.userinfo.split(':')[0] : options.values_at(:user)
15
- @password = @uri.userinfo ? @user.userinfo.split(':')[1] : options.values_at(:password)
14
+ @user = @uri.userinfo ? @user.userinfo.split(':')[0] : username
15
+ @password = @uri.userinfo ? @user.userinfo.split(':')[1] : password
16
16
  @port = @uri.port || @uri.default_port
17
17
 
18
18
  if block_given?
@@ -3,62 +3,43 @@ require 'net/https'
3
3
  require 'rexchange/dav_search_request'
4
4
  require 'rexchange/message'
5
5
  require 'rexchange/contact'
6
+ require 'rexchange/appointment'
6
7
 
7
8
  module RExchange
8
9
 
10
+ class FolderNotFoundError < StandardError
11
+ end
12
+
9
13
  class Folder
10
14
  include REXML
11
15
 
12
- include Enumerable
13
-
14
- attr_reader :credentails
16
+ attr_reader :credentails, :name
15
17
 
16
- def initialize(credentials, parent, folder)
17
- @credentials, @parent, @folder = credentials, parent, folder
18
+ def initialize(credentials, parent, name, content_type)
19
+ @credentials, @parent, @name = credentials, parent, name
20
+ @content_type = CONTENT_TYPES[content_type]
18
21
  end
19
22
 
20
- alias :old_method_missing :method_missing
21
-
22
- # Used to access subfolders. If the subfolder does not
23
- # exist, then old_method_missing is called.
23
+ # Used to access subfolders.
24
24
  def method_missing(sym, *args)
25
25
  if folders.has_key?(sym.to_s)
26
- Folder.new(@credentials, self, folders[sym.to_s] )
26
+ folders[sym.to_s]
27
27
  else
28
- old_method_missing(sym, args)
28
+ raise FolderNotFoundError.new("#{sym} is not a subfolder of #{name}")
29
29
  end
30
30
  end
31
31
 
32
- # Iterate through each RExchange::Message in this folder
32
+ include Enumerable
33
+
34
+ # Iterate through each entry in this folder
33
35
  def each
34
- if @folder =~ /^contacts$/i || @parent =~ /\/contacts\//i
35
- get_contacts
36
- else
37
- get_messages
38
- end.each do |item|
36
+ @content_type::find(@credentials, to_s).each do |item|
39
37
  yield item
40
- end
41
- end
42
-
43
- # Retrieve an Array of messages from a specific folder
44
- # === Example
45
- # RExchange::open(uri, :user => 'bob', :password => 'random') do |mailbox|
46
- # mailbox.messages_in('inbox/archive').each do |message|
47
- # p message.from
48
- # end
49
- # end
50
- def messages_in(folder)
51
- folder.split('/').inject(@credentials.uri.path) do |final_path, current_path|
52
- Folder.new(@credentials, final_path, current_path)
53
- end.get_messages
54
- end
55
-
56
- def get_messages
57
- RExchange::Message::find(@credentials, to_s)
38
+ end
58
39
  end
59
40
 
60
- def get_contacts
61
- RExchange::Contact::find(@credentials, to_s)
41
+ def search(conditions = {})
42
+ @content_type::find(@credentials, to_s, conditions)
62
43
  end
63
44
 
64
45
  # Join the strings passed in with '/'s between them
@@ -68,40 +49,39 @@ module RExchange
68
49
 
69
50
  # Return an Array of subfolders for this folder
70
51
  def folders
71
- @folders ||= get_folders
52
+ @folders ||= begin
53
+ request_body = <<-eos
54
+ <?xml version="1.0"?>
55
+ <D:searchrequest xmlns:D = "DAV:">
56
+ <D:sql>
57
+ SELECT "DAV:displayname", "DAV:contentclass"
58
+ FROM SCOPE('shallow traversal of "#{to_s}"')
59
+ WHERE "DAV:ishidden" = false
60
+ AND "DAV:isfolder" = true
61
+ </D:sql>
62
+ </D:searchrequest>
63
+ eos
64
+
65
+ response = DavSearchRequest.execute(@credentials, :body => request_body)
66
+
67
+ folders = {}
68
+
69
+ # iterate through folders query and add a new Folder
70
+ # object for each, under a normalized name.
71
+ xpath_query = "//a:propstat[a:status/text() = 'HTTP/1.1 200 OK']/a:prop"
72
+ Document.new(response.body).elements.each(xpath_query) do |m|
73
+ displayname = m.elements['a:displayname'].text
74
+ contentclass = m.elements['a:contentclass'].text
75
+ folders[displayname.normalize] = Folder.new(@credentials, self, displayname, contentclass.split(':').last.sub(/folder$/, ''))
76
+ end
77
+
78
+ folders
79
+ end
72
80
  end
73
81
 
74
82
  # Return the absolute path to this folder (but not the full URI)
75
83
  def to_s
76
- Folder.join(@parent, @folder)
77
- end
78
-
79
- private
80
-
81
- def get_folders
82
- request_body = <<DA_QUERY
83
- <?xml version="1.0"?>
84
- <D:searchrequest xmlns:D = "DAV:">
85
- <D:sql>
86
- SELECT "DAV:displayname"
87
- FROM SCOPE('shallow traversal of "#{to_s}"')
88
- WHERE "DAV:ishidden" = false
89
- AND "DAV:isfolder" = true
90
- </D:sql>
91
- </D:searchrequest>
92
- DA_QUERY
93
-
94
- response = DavSearchRequest.execute(@credentials, :body => request_body)
95
-
96
- folders = {}
97
-
98
- # iterate through folders query and add each normalized name to the folders array.
99
- xpath_query = "//a:propstat[a:status/text() = 'HTTP/1.1 200 OK']/a:prop"
100
- Document.new(response.body).elements.each(xpath_query) do |m|
101
- folders[m.elements['a:displayname'].text.normalize] = m.elements['a:displayname'].text
102
- end
103
-
104
- return folders
84
+ Folder.join(@parent, @name)
105
85
  end
106
86
  end
107
87
  end
@@ -4,61 +4,120 @@ require 'rexchange/dav_move_request'
4
4
  require 'time'
5
5
 
6
6
  module RExchange
7
+
8
+ class Folder
9
+ CONTENT_TYPES = {}
10
+ end
11
+
7
12
  class GenericItem
8
13
  include REXML
9
14
  include Enumerable
10
-
11
- attr_accessor :attributes
12
-
13
- # Used to access the attributes of the item as a hash.
14
- def [](key)
15
- return @attributes[key]
16
- end
17
-
18
- # Used to access the attributes of the item.
19
- def method_missing(sym, *args)
20
- return @attributes[sym.to_s] if @attributes.include?(sym.to_s)
21
- end
22
-
15
+
16
+ attr_accessor :attributes
17
+
23
18
  def initialize(session, dav_property_node)
24
19
  @attributes = {}
25
20
  @session = session
26
-
21
+
27
22
  dav_property_node.elements.each do |element|
28
- if element.name.normalize =~ /date$/i
29
- @attributes[element.name.normalize] = Time::parse(element.text)
23
+ namespaced_name = element.namespace + element.name
24
+
25
+ if element.name =~ /date$/i || self.class::ATTRIBUTE_MAPPINGS.find { |k,v| v == namespaced_name && k.to_s =~ /\_(at|on)$/ }
26
+ @attributes[namespaced_name] = Time::parse(element.text)
30
27
  else
31
- @attributes[element.name.normalize] = element.text
28
+ @attributes[namespaced_name] = element.text
32
29
  end
33
30
  end
31
+ end
32
+
33
+ # Set the default CONTENT_CLASS to the class name, and define a
34
+ # dynamic query method for the derived class.
35
+ def self.inherited(base)
36
+ base.const_set('CONTENT_CLASS', base.to_s.split('::').last.downcase)
34
37
 
35
- return self
38
+ def base.query(path)
39
+ <<-QBODY
40
+ SELECT
41
+ #{self::ATTRIBUTE_MAPPINGS.values.map { |f| '"' + f + '"' }.join(',')}
42
+ FROM SCOPE('shallow traversal of "#{path}"')
43
+ WHERE "DAV:ishidden" = false
44
+ AND "DAV:isfolder" = false
45
+ AND "DAV:contentclass" = 'urn:content-classes:#{self::CONTENT_CLASS}'
46
+ QBODY
47
+ end
36
48
  end
37
49
 
50
+ # This handy method is meant to be called from any inheriting
51
+ # classes. It is used to bind types of folders to particular
52
+ # Entity classes so that the folder knows what type it's
53
+ # enumerating. So for a "calendarfolder" you'd call:
54
+ # set_folder_type 'calendarfolder' # or just 'calendar'
55
+ def self.set_folder_type(dav_name)
56
+ Folder::CONTENT_TYPES[dav_name.sub(/folder$/, '')] = self
57
+ end
58
+
59
+ # --Normally Not Used--
60
+ # By default the CONTENT_CLASS is determined by the name
61
+ # of your class. So for the Appointment class the
62
+ # CONTENT_CLASS would be 'appointment'.
63
+ # If for some reason this convention doesn't suit you,
64
+ # you can use this method to set the appropriate value
65
+ # (which is used in queries).
66
+ # For example, the DAV:content-class for contacts is:
67
+ # 'urn:content-classes:person'
68
+ # Person doesn't strike me as the best name for our class though.
69
+ # Most people would refer to an entry in a Contacts folder as
70
+ # a Contact. So that's what we call our class, and we use this method
71
+ # to make sure everything still works as it should.
72
+ def self.set_content_class(dav_name)
73
+ const_set('CONTENT_CLASS', dav_name)
74
+ end
75
+
76
+ # Defines what attributes are used in queries, and
77
+ # what methods they map to in instances. You should
78
+ # pass a Hash of method_name and namespaced-attribute-name pairs.
38
79
  def self.attribute_mappings(mappings)
80
+
81
+ mappings.merge! :uid => 'DAV:uid',
82
+ :modified_at => 'DAV:getlastmodified',
83
+ :href => 'DAV:href'
84
+
85
+ const_set('ATTRIBUTE_MAPPINGS', mappings)
86
+
39
87
  mappings.each_pair do |k,v|
40
- define_method k do
88
+
89
+ define_method(k) do
41
90
  @attributes[v]
42
91
  end
92
+
93
+ define_method("#{k.to_s.sub(/\?$/, '')}=") do |value|
94
+ @attributes[v] = value
95
+ end
96
+
43
97
  end
44
98
  end
45
99
 
46
- def self.query(path)
47
- raise 'YOU MUST DEFINE THIS'
48
- end
49
-
50
100
  # Retrieve an Array of items (such as Contact, Message, etc)
51
- def self.find(credentials, path)
52
- response = DavSearchRequest.execute(credentials, :body => query(path))
101
+ def self.find(credentials, path, conditions = nil)
102
+ qbody = <<-QBODY
103
+ <?xml version="1.0"?>
104
+ <D:searchrequest xmlns:D = "DAV:">
105
+ <D:sql>
106
+ #{conditions.nil? ? query(path) : search(path, conditions)}
107
+ </D:sql>
108
+ </D:searchrequest>
109
+ QBODY
53
110
 
111
+ response = DavSearchRequest.execute(credentials, :body => qbody)
112
+
54
113
  items = []
55
114
  xpath_query = "//a:propstat[a:status/text() = 'HTTP/1.1 200 OK']/a:prop"
56
-
115
+
57
116
  Document.new(response.body).elements.each(xpath_query) do |m|
58
117
  items << self.new(credentials, m)
59
118
  end
60
-
61
- return items
119
+
120
+ items
62
121
  end
63
122
  end
64
123
  end
@@ -3,17 +3,17 @@ require 'rexchange/generic_item'
3
3
  module RExchange
4
4
  class Message < GenericItem
5
5
 
6
- def to_s
7
- "To: #{to}, From: #{from}, Subject: #{subject}"
8
- end
6
+ set_folder_type 'mail'
9
7
 
10
- attribute_mappings 'has_attachments?' => 'hasattachment'
11
-
12
- # Returns the body of the message. This is either a httpmail:textdescription,
13
- # or a httpmail:htmldescription
14
- def body
15
- @attributes['textdescription'] || @attributes['htmldescription']
16
- end
8
+ attribute_mappings :from => 'urn:schemas:httpmail:from',
9
+ :to => 'urn:schemas:httpmail:to',
10
+ :message_id => 'urn:schemas:mailheader:message-id',
11
+ :subject => 'urn:schemas:httpmail:subject',
12
+ :recieved_on => 'urn:schemas:httpmail:date',
13
+ :importance => 'urn:schemas:httpmail:importance',
14
+ :has_attachments? => 'urn:schemas:httpmail:hasattachment',
15
+ :body => 'urn:schemas:httpmail:textdescription',
16
+ :html => 'urn:schemas:httpmail:htmldescription'
17
17
 
18
18
  # Move this message to the specified folder.
19
19
  # The folder can be a string such as 'inbox/archive' or a RExchange::Folder.
@@ -28,28 +28,15 @@ module RExchange
28
28
  else
29
29
  @session.uri.path.ensure_ends_with('/') + folder.to_s.ensure_ends_with('/') + source.split('/').last
30
30
  end
31
-
31
+
32
32
  $log.debug "move_to: source => \"#{source}\", destination => \"#{destination}\""
33
33
  DavMoveRequest.execute(@session, source, destination)
34
34
  end
35
35
 
36
- def self.query(path)
37
- <<-QBODY
38
- <?xml version="1.0"?>
39
- <D:searchrequest xmlns:D = "DAV:">
40
- <D:sql>
41
- SELECT "DAV:href", "urn:schemas:httpmail:from", "urn:schemas:httpmail:to",
42
- "urn:schemas:mailheader:message-id", "urn:schemas:httpmail:subject",
43
- "urn:schemas:httpmail:date", "urn:schemas:httpmail:importance",
44
- "urn:schemas:httpmail:hasattachment", "urn:schemas:httpmail:textdescription",
45
- "urn:schemas:httpmail:htmldescription"
46
- FROM SCOPE('shallow traversal of "#{path}"')
47
- WHERE "DAV:ishidden" = false
48
- AND "DAV:isfolder" = false
49
- AND "DAV:contentclass" = 'urn:content-classes:message'
50
- </D:sql>
51
- </D:searchrequest>
52
- QBODY
36
+
37
+ def to_s
38
+ "To: #{to}, From: #{from}, Subject: #{subject}"
53
39
  end
40
+
54
41
  end
55
42
  end
@@ -11,14 +11,14 @@ module RExchange
11
11
  # uri = 'https://mydomain.com/exchange/demo'
12
12
  # options = { :user => 'test', :password => 'random' }
13
13
  #
14
- # RExchange::Session.new(uri, options) do |mailbox|
14
+ # RExchange::Session.new(uri, 'bob', 'secret') do |mailbox|
15
15
  # mailbox.test.each do |message|
16
16
  # puts message.subject
17
17
  # end
18
18
  # end
19
- def initialize(uri, options = {})
19
+ def initialize(uri, username = nil, password = nil)
20
20
 
21
- @credentials = Credentials.new(uri, options)
21
+ @credentials = Credentials.new(uri, username, password)
22
22
  @parent = @credentials.uri.path
23
23
  @folder = ''
24
24
 
@@ -4,24 +4,19 @@ require 'rexchange'
4
4
  class FunctionalTests < Test::Unit::TestCase
5
5
 
6
6
  def setup
7
+ @mailbox = RExchange::Session.new 'url', 'username', 'password'
7
8
  end
8
9
 
9
10
  def teardown
11
+ @mailbox = nil
10
12
  end
11
13
 
12
14
  # Ok, so it's not a real test, but I needed to get started,
13
15
  # and get rid of console scripts.
14
16
  def test_no_exceptions
15
-
16
- uri = "https://#{ENV['rexchange_test_server']}/exchange/#{ENV['rexchange_test_mailbox']}/"
17
- options = { :user => ENV['rexchange_test_user'], :password => ENV['rexchange_test_password'] }
18
17
 
19
- RExchange::open(uri, options) do |mailbox|
20
- mailbox.inbox.each do |message|
21
- puts message.body
22
- end
23
-
24
- mailbox.folders.each { |folder| puts folder }
18
+ @mailbox.inbox.search(:from => 'scott').each do |m|
19
+ puts m.subject
25
20
  end
26
21
 
27
22
  end
metadata CHANGED
@@ -1,60 +1,67 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.10
2
+ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: rexchange
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2006-02-23
8
- summary: "A simple wrapper around Microsoft Exchange Server's WebDAV API"
6
+ version: 0.3.0
7
+ date: 2007-01-05 00:00:00 -06:00
8
+ summary: A simple wrapper around Microsoft Exchange Server's WebDAV API
9
9
  require_paths:
10
- - lib
10
+ - lib
11
11
  email: ssmoot@gmail.com; bauer.mail@gmail.com
12
12
  homepage: http://substantiality.net
13
13
  rubyforge_project: rexchange
14
- description: "Connect, browse, and iterate through folders and messages on an Exchange Server"
14
+ description: Connect, browse, and iterate through folders and messages on an Exchange Server
15
15
  autorequire: rexchange
16
16
  default_executable:
17
17
  bindir: bin
18
18
  has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
- -
22
- - ">"
23
- - !ruby/object:Gem::Version
24
- version: 0.0.0
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
25
24
  version:
26
25
  platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
27
29
  authors:
28
- - Sam Smoot
29
- - Scott Bauer
30
+ - Sam Smoot
31
+ - Scott Bauer
30
32
  files:
31
- - README
32
- - CHANGELOG
33
- - RAKEFILE
34
- - lib/r_exchange.rb
35
- - lib/rexchange.rb
36
- - lib/rexchange/contact.rb
37
- - lib/rexchange/credentials.rb
38
- - lib/rexchange/dav_move_request.rb
39
- - lib/rexchange/dav_search_request.rb
40
- - lib/rexchange/exchange_request.rb
41
- - lib/rexchange/folder.rb
42
- - lib/rexchange/generic_item.rb
43
- - lib/rexchange/message.rb
44
- - lib/rexchange/session.rb
45
- - test/functional.rb
33
+ - README
34
+ - CHANGELOG
35
+ - RAKEFILE
36
+ - lib/rexchange.rb
37
+ - lib/r_exchange.rb
38
+ - lib/rexchange/appointment.rb
39
+ - lib/rexchange/contact.rb
40
+ - lib/rexchange/credentials.rb
41
+ - lib/rexchange/dav_move_request.rb
42
+ - lib/rexchange/dav_search_request.rb
43
+ - lib/rexchange/exchange_request.rb
44
+ - lib/rexchange/folder.rb
45
+ - lib/rexchange/generic_item.rb
46
+ - lib/rexchange/message.rb
47
+ - lib/rexchange/session.rb
48
+ - test/functional.rb
46
49
  test_files: []
50
+
47
51
  rdoc_options:
48
- - "--line-numbers"
49
- - "--inline-source"
50
- - "--main"
51
- - README
52
+ - --line-numbers
53
+ - --inline-source
54
+ - --main
55
+ - README
52
56
  extra_rdoc_files:
53
- - README
54
- - CHANGELOG
55
- - RAKEFILE
57
+ - README
58
+ - CHANGELOG
59
+ - RAKEFILE
56
60
  executables: []
61
+
57
62
  extensions: []
63
+
58
64
  requirements:
59
- - none
60
- dependencies: []
65
+ - none
66
+ dependencies: []
67
+