garlenko 0.0.1

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/.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: