garlenko 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spec/credentials.yml
19
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in garlenko.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,22 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^spec/.+_spec\.rb$})
11
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
12
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
13
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
14
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
15
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
16
+ watch('spec/spec_helper.rb') { "spec" }
17
+ watch('config/routes.rb') { "spec/routing" }
18
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
19
+ # Capybara request specs
20
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
21
+ end
22
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/garlenko.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/garlenko/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Lucas Florio"]
6
+ gem.email = ["lucasefe@gmail.com"]
7
+ gem.description = %q{contacts importer from Live (Hotmail), Gmail, and
8
+ Yahoo, using mechanize and nokogiri. }
9
+ gem.summary = %q{contacts importer from Live (Hotmail), Gmail, and
10
+ Yahoo, using mechanize and nokogiri. }
11
+ gem.homepage = ""
12
+
13
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ gem.files = `git ls-files`.split("\n")
15
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ gem.name = "garlenko"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = Garlenko::VERSION
19
+ gem.add_dependency "mechanize"
20
+ gem.add_dependency "nokogiri"
21
+ gem.add_dependency "gdata_19", [">= 1.1.3"]
22
+ gem.add_development_dependency "rspec", [">= 2.0.0"]
23
+ gem.add_development_dependency "guard-rspec"
24
+ gem.add_development_dependency "growl"
25
+ end
@@ -0,0 +1,29 @@
1
+
2
+ module Garlenko
3
+
4
+ def self.new(kind, username, password)
5
+ case kind
6
+ when :live then Live.new(username, password)
7
+ when :gmail then Gmail.new(username, password)
8
+ else
9
+ raise "Invalid kind (#{kind})!"
10
+ end
11
+ end
12
+
13
+
14
+ class Base
15
+ attr_accessor :username
16
+ attr_writer :password
17
+
18
+ def initialize(username, password)
19
+ @username = username
20
+ @password = password
21
+ @connected = false
22
+ end
23
+
24
+ def connected?
25
+ @connected
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,45 @@
1
+ require 'gdata'
2
+
3
+ module Garlenko
4
+
5
+ class Gmail < Base
6
+
7
+ CONTACTS_SCOPE = 'http://www.google.com/m8/feeds/'
8
+ CONTACTS_FEED = CONTACTS_SCOPE + 'contacts/default/full/?max-results=1000'
9
+
10
+ def contacts
11
+ connect!
12
+ feed = @client.get(CONTACTS_FEED).to_xml
13
+
14
+ @contacts = feed.elements.to_a('entry').collect do |entry|
15
+ title, email, photo = entry.elements['title'].text, nil, [nil, nil]
16
+
17
+ begin
18
+ entry.elements.each('link[@gd:etag]') do |e|
19
+ gdata_response = @client.get(e.attribute('href').value)
20
+ photo = [gdata_response.body, gdata_response.headers['content-type']] if gdata_response.status_code == 200 and !gdata_response.body.nil?
21
+ end
22
+ rescue StandardError
23
+ photo = [nil, nil]
24
+ end
25
+
26
+ entry.elements.each('gd:email') do |e|
27
+ email = e.attribute('address').value if e.attribute('primary')
28
+ end
29
+ { :name => title, :email => email, :photo => photo } unless email.nil?
30
+ end
31
+ @contacts.compact!
32
+ @connected = true
33
+ return @contacts if @contacts
34
+ end
35
+
36
+ def connect!
37
+ @client = GData::Client::Contacts.new
38
+ @client.clientlogin(@username, @password, @captcha_token, @captcha_response)
39
+ @connected = true
40
+ rescue GData::Client::AuthorizationError => e
41
+ @connected = false
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,76 @@
1
+ module Garlenko
2
+
3
+ class Live < Base
4
+
5
+ attr_accessor :last_result
6
+
7
+ LOGIN_URL = "https://mid.live.com/si/login.aspx"
8
+ CONTACTS_URL = "http://mpeople.live.com"
9
+
10
+ def agent
11
+ @agent ||= Mechanize.new { |agent|
12
+ agent.user_agent = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
13
+ agent.follow_meta_refresh = true
14
+ }
15
+ end
16
+
17
+ def connect!(force_connect = false)
18
+ return @connected unless !connected? or force_connect
19
+ login_form = agent.get(LOGIN_URL).form('EmailPasswordForm')
20
+ login_form.LoginTextBox = @username
21
+ login_form.PasswordTextBox = @password
22
+ login_res = agent.submit(login_form, login_form.submits.first)
23
+ @connected = !login_res.body.include?("NotificationContainerError")
24
+ end
25
+
26
+ def contacts
27
+ connect! unless connected?
28
+ pool = []
29
+ contacts_so_far = []
30
+
31
+ on_contacts_page CONTACTS_URL do |url, page|
32
+ pool << Thread.new(url, page) do |u, p|
33
+ fetch_contacts_from(p)
34
+ end
35
+ end
36
+ contacts_so_far = pool.each(&:join).map(&:value).flatten
37
+ puts "fetched: #{contacts_so_far.size}"
38
+ contacts_so_far
39
+ end
40
+
41
+ def on_contacts_page(url, &block)
42
+ unless url.nil?
43
+ page = agent.get(url)
44
+ block.call(url, page)
45
+ next_path = next_page_link_for(page)
46
+ next_url = next_path.nil? ? nil : CONTACTS_URL + next_path.href
47
+ on_contacts_page(next_url, &block)
48
+ end
49
+ end
50
+
51
+ def next_page_link_for(page)
52
+ page.links_with(:id => /ContactList_next/).first
53
+ end
54
+
55
+ def fetch_contacts_from(page)
56
+ page.links_with(:href => /contactinfo.aspx/).map do |link|
57
+ fetch_contact_info_from link.href
58
+ end.flatten
59
+ end
60
+
61
+ def fetch_contact_info_from(path)
62
+ url = CONTACTS_URL + path
63
+ doc = Nokogiri::HTML(agent.get(url).body)
64
+ name = doc.css('#title').first.content
65
+ emails = doc.css("#elklid, #elkps, #elkot, #emtot, #elkim")
66
+ unless emails.empty?
67
+ emails.map do |email|
68
+ { :name => name, :email => email.content }
69
+ end
70
+ else
71
+ []
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module Garlenko
2
+ VERSION = "0.0.1"
3
+ end
data/lib/garlenko.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'mechanize'
2
+ require 'nokogiri'
3
+ require "garlenko/version"
4
+ require "garlenko/base"
5
+ require "garlenko/live"
6
+ require "garlenko/gmail"
@@ -0,0 +1,3 @@
1
+ live:
2
+ username: email here
3
+ password: password here
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "any consumer" do
4
+
5
+ it "should login" do
6
+ garlenko.connect!
7
+ garlenko.should be_connected
8
+ end
9
+
10
+ it "should NOT login with invalid data" do
11
+ garlenko.password = "INVALID"
12
+ garlenko.connect!
13
+ garlenko.should_not be_connected
14
+ end
15
+
16
+ it "should fetch the contacts" do
17
+ contacts = garlenko.contacts
18
+ contacts.should be_a_kind_of(Array)
19
+ contacts.should_not be_empty
20
+ contact = contacts.first
21
+ contact[:email].should_not be_nil
22
+ end
23
+ end
24
+
25
+ describe Garlenko do
26
+ context "against Gmail" do
27
+
28
+ let(:garlenko) do
29
+ gmail = load_credentials.gmail
30
+ Garlenko.new :gmail, gmail["username"], gmail['password']
31
+ end
32
+
33
+ it "should construct a valid garlenko consumer" do
34
+ garlenko.should be_a_kind_of(Garlenko::Gmail)
35
+ end
36
+
37
+ it_should_behave_like "any consumer"
38
+ end
39
+
40
+ context "against Live/Hotmail and the like" do
41
+
42
+ let(:garlenko) do
43
+ live = load_credentials.live
44
+ Garlenko.new :live, live["username"], live['password']
45
+ end
46
+
47
+ it "should construct a valid garlenko consumer" do
48
+ garlenko.should be_a_kind_of(Garlenko::Live)
49
+ end
50
+
51
+ it_should_behave_like "any consumer"
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ require 'garlenko'
2
+ require 'rspec'
3
+
4
+ Dir[File.join(Dir.pwd, "spec/support/**/*.rb")].each {|f| require f}
5
+
@@ -0,0 +1,18 @@
1
+ require "ostruct"
2
+ require "yaml"
3
+
4
+ module Helpers
5
+
6
+ def load_credentials
7
+ credentials_file = File.join(Dir.pwd, "/spec/credentials.yml")
8
+ if File.exists?(credentials_file)
9
+ conf = YAML.load(File.read("#{Dir.pwd}/spec/credentials.yml"))
10
+ OpenStruct.new(conf)
11
+ else
12
+ raise "missing credentials file. "
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ RSpec.configuration.include Helpers
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: garlenko
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lucas Florio
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mechanize
16
+ requirement: &70274506739960 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70274506739960
25
+ - !ruby/object:Gem::Dependency
26
+ name: nokogiri
27
+ requirement: &70274506738660 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70274506738660
36
+ - !ruby/object:Gem::Dependency
37
+ name: gdata_19
38
+ requirement: &70274506758120 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 1.1.3
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70274506758120
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70274506768040 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 2.0.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70274506768040
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-rspec
60
+ requirement: &70274506766420 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70274506766420
69
+ - !ruby/object:Gem::Dependency
70
+ name: growl
71
+ requirement: &70274506763520 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70274506763520
80
+ description: ! "contacts importer from Live (Hotmail), Gmail, and\n Yahoo, using
81
+ mechanize and nokogiri. "
82
+ email:
83
+ - lucasefe@gmail.com
84
+ executables: []
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - .gitignore
89
+ - Gemfile
90
+ - Guardfile
91
+ - Rakefile
92
+ - garlenko.gemspec
93
+ - lib/garlenko.rb
94
+ - lib/garlenko/base.rb
95
+ - lib/garlenko/gmail.rb
96
+ - lib/garlenko/live.rb
97
+ - lib/garlenko/version.rb
98
+ - spec/credentials.yml.example
99
+ - spec/lib/garlenko_spec.rb
100
+ - spec/spec_helper.rb
101
+ - spec/support/helpers.rb
102
+ homepage: ''
103
+ licenses: []
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 1.8.10
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: contacts importer from Live (Hotmail), Gmail, and Yahoo, using mechanize
126
+ and nokogiri.
127
+ test_files:
128
+ - spec/credentials.yml.example
129
+ - spec/lib/garlenko_spec.rb
130
+ - spec/spec_helper.rb
131
+ - spec/support/helpers.rb
132
+ has_rdoc: