GitHub Sale: sign up for any paid plan this week and pay nothing until January 1, 2009!  [ hide ]

public
Description: DataMapper Adapters
Homepage: http://www.yehudakatz.com
Clone URL: git://github.com/wycats/dm-adapters.git
dm-adapters / imap / adapter.rb
100644 113 lines (91 sloc) 3.532 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
require "rubygems"
gem "dm-core"
require "data_mapper"
require "net/imap"
require "#{File.dirname(__FILE__)}/types"
require "#{File.dirname(__FILE__)}/typecast"
 
# DataMapper.setup(:default, "imap://wycats%40gmail.com:pass@imap.gmail.com/INBOX")
 
module DataMapper
  module Adapters
    
    class ImapAdapter < AbstractAdapter
      def connect
        @imap = Net::IMAP.new(@uri.host, 993, true)
        begin
          @imap.send(:send_command, "authenticate", "login")
        rescue
        ensure
          @imap.send(:send_command, "login", URI.unescape(@uri.user), @uri.password)
        end
        @imap.select(@uri.path.gsub(%r{^/}, ""))
      end
            
      def read(repository, resource, key)
        properties = resource.properties(name).defaults
        properties_with_indexes = Hash[*properties.zip((0...properties.size).to_a).flatten]
 
        set = Collection.new(repository, resource, properties_with_indexes)
        
        connect
        
        imap_results = @imap.uid_fetch(key, imap_props(properties))
        materialize_imap_results(set, imap_results, properties_with_indexes)
        set.first
      end
      
      def read_set(repository, query, one = false)
        properties = query.fields
        properties_with_indexes = Hash[*properties.zip((0...properties.size).to_a).flatten]
 
        set = Collection.new(repository, query.model, properties_with_indexes)
        
        query_array = query_to_array(query)
        query_array.unshift "ALL"
        
        begin
          connect unless @imap
          if one
            imap_results = @imap.fetch(1, imap_props(properties))
          else
            imap_seqs = @imap.search(query_array)
            puts "@imap.fetch(#{imap_seqs.inspect}, #{imap_props(properties).inspect})"
            imap_results = @imap.fetch(imap_seqs, imap_props(properties))
          end
        rescue Net::IMAP::NoResponseError
          connect
          retry
        end
        
        materialize_imap_results(set, imap_results, properties_with_indexes, query.reload?)
        set
      end
      
      def read_one(repository, query)
        read_set(repository, query, true).first
      end
      
      def query_to_array(query)
        result = []
        query.conditions.each do |op, prop, val|
          result += (prop.type.query_details[op] + [typecast_dump(val)])
        end
        result
      end
      
      def materialize_imap_results(set, results, properties_with_indexes, reload = false)
        results.each do |result|
          props = properties_with_indexes.inject([]) do |accum, prop_idx|
            prop, idx = prop_idx
            prop_result = result.attr[prop.field.upcase]
            prop_result = prop_result.send(prop.type.envelope_name) if prop.type.envelope?
            accum[idx] = typecast_load(prop_result, prop)
            accum
          end
          set.load props, reload
        end
      end
      
      def imap_props(properties)
        properties.map {|prop| prop.field.upcase}.uniq
      end
      
    end
    
  end
end
 
ImapTypes = DataMapper::Adapters::Imap
 
class Gmail
  include DataMapper::Resource
  
  property :id, ImapTypes::Uid, :key => true
  property :subject, ImapTypes::Subject
  property :sender, ImapTypes::Sender
  property :date, ImapTypes::InternalDate
  property :body, ImapTypes::Body, :lazy => true
 
  def inspect
    self.class.properties(:default).inject({}) {|s,x| s.merge(x.name => self.instance_variable_get("@#{x.name}"))}.inspect
  end
end