rexchange 0.2.0 → 0.3.0

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