wagon 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ spec/user.dat
23
+ *.pdf
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Devin Christensen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,7 @@
1
+ = Wagon
2
+
3
+ Provided a valid lds.org username and password Wagon will download the information and pictures from the "Photo Directory" page and compile it into a convenient PDF.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 Devin Christensen. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "wagon"
8
+ gem.summary = %Q{Create a PDF from the lds.org ward Photo Directory.}
9
+ gem.description = %Q{Provided a valid lds.org username and password, Wagon will download all the information from the Photo Directory page and compile it into a convenient PDF.}
10
+ gem.email = "devin@threetrieslater.com"
11
+ gem.homepage = "http://github.com/threetrieslater/wagon"
12
+ gem.authors = ["Devin Christensen"]
13
+ gem.bindir = 'bin'
14
+ gem.add_dependency "nokogiri", ">= 1.4.0"
15
+ gem.add_dependency "highline", ">= 1.5.1"
16
+ gem.add_dependency "prawn", ">= 0.5.1"
17
+ gem.add_development_dependency "rspec", ">= 1.2.9"
18
+ gem.add_development_dependency "yard", ">= 0"
19
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
24
+ end
25
+
26
+ require 'spec/rake/spectask'
27
+ Spec::Rake::SpecTask.new(:spec) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.spec_files = FileList['spec/**/*_spec.rb']
30
+ end
31
+
32
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
33
+ spec.libs << 'lib' << 'spec'
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+ task :spec => :check_dependencies
39
+
40
+ task :default => :spec
41
+
42
+ begin
43
+ require 'yard'
44
+ YARD::Rake::YardocTask.new
45
+ rescue LoadError
46
+ task :yardoc do
47
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
48
+ end
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.0
data/bin/wagon ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # == Synopsis
4
+ # Create a PDF from your wards photo directory on lds.org
5
+ #
6
+ # == Examples
7
+ # Output a file named "photo_directory.pdf" to the current directory.
8
+ # wagon
9
+ #
10
+ # Other examples:
11
+ # wagon --title="Ward Menu"
12
+ #
13
+ # == Usage
14
+ # wagon [options] [output_file]
15
+ #
16
+ # == Options
17
+ # -h, --help Displays help message
18
+ # -v, --version Display the version, then exit
19
+ # -V, --verbose Verbose output
20
+ # -t, --title Override the default title with your own
21
+ # -r, --rows Number of rows per page (default is 6)
22
+ # -c, --columns Number of columns per page (default is 7)
23
+ # -p, --padding Padding between households (default is 2)
24
+ # -f, --font-size Primary font size (default is 8)
25
+ # --page-numbers Include page numbers in the footer, e.g. (1 of 3)
26
+ # --date Include the current date in the footer
27
+ # --no-picture Do not include pictures
28
+ # --no-address Do not include street addresses
29
+ # --no-phone Do not include phone numbers
30
+ # --no-email Do not include email addresses
31
+ #
32
+ # == Copyright
33
+ # Copyright (c) 2009 Devin Christensen. Licensed under the MIT License:
34
+ # http://www.opensource.org/licenses/mit-license.php
35
+
36
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
37
+
38
+ require 'optparse'
39
+ require 'rdoc/usage'
40
+ require 'ostruct'
41
+ require 'wagon'
42
+ require 'highline/import'
43
+
44
+ class WagonApp
45
+ def initialize(arguments, stdin)
46
+ @arguments = arguments
47
+ @stdin = stdin
48
+ @options = OpenStruct.new()
49
+
50
+ # Set defaults
51
+ @options.verbose = false
52
+ @options.title = nil
53
+ @options.rows = 6
54
+ @options.columns = 7
55
+ @options.padding = 2
56
+ @options.font_size = 8
57
+ @options.page_numbers = false
58
+ @options.include_date = false
59
+ @options.picture = true
60
+ @options.address = true
61
+ @options.phone_number = true
62
+ @options.email = true
63
+ @options.output_file = "./photo_directory.pdf"
64
+ end
65
+
66
+ def run
67
+ if arguments_valid?
68
+ puts "Start at #{DateTime.now}\n\n" if @options.verbose
69
+
70
+ output_options if @options.verbose # [Optional]
71
+
72
+ process
73
+
74
+ puts "\nFinished in #{DateTime.now}" if @options.verbose
75
+
76
+ else
77
+ output_usage
78
+ end
79
+
80
+ end
81
+
82
+ protected
83
+ def arguments_valid?
84
+ # Specify options
85
+ opts = OptionParser.new
86
+ opts.on('-v', '--version') { output_version ; exit 0 }
87
+ opts.on('-h', '--help') { output_help }
88
+ opts.on('-V', '--verbose') { @options.verbose = true }
89
+ opts.on('-t', '--title=TITLE') { |title| @options.title = title }
90
+ opts.on('-r', '--rows=ROWS') { |rows| @options.rows = rows }
91
+ opts.on('-c', '--columns=COLUMNS') { |columns| @options.columns = columns }
92
+ opts.on('-p', '--padding=PADDING') { |padding| @options.padding = padding }
93
+ opts.on('-f', '--font-size=SIZE') { |size| @options.font_size = size }
94
+ opts.on('--page-numbers') { @options.page_numbering = true }
95
+ opts.on('--date') { @options.include_date = true }
96
+ opts.on('--no-picture') { @options.picture = false }
97
+ opts.on('--no-address') { @options.address = false }
98
+ opts.on('--no-phone') { @options.phone_number = false }
99
+ opts.on('--no-email') { @options.email = false }
100
+
101
+ opts.parse!(@arguments) rescue return false
102
+ @options.output_file = @arguments.last unless @arguments.empty?
103
+
104
+ true
105
+ end
106
+
107
+ def output_options
108
+ puts "Options:\n"
109
+
110
+ @options.marshal_dump.each do |name, val|
111
+ puts " #{name} = #{val}"
112
+ end
113
+ end
114
+
115
+ def output_help
116
+ output_version
117
+ RDoc::usage()
118
+ end
119
+
120
+ def output_usage
121
+ RDoc::usage('usage') # gets usage from comments above
122
+ end
123
+
124
+ def output_version
125
+ puts "#{File.basename(__FILE__)} version #{Wagon::VERSION}"
126
+ end
127
+
128
+ def process
129
+ username = ask("What is your lds.org username? ")
130
+ password = ask("What is your lds.org password? ") { |q| q.echo = "*" }
131
+
132
+ user = Wagon::connect(username, password)
133
+
134
+ puts "\nAlright, we're in!"
135
+ puts "I'm gonna go ahead and create that PDF for ya now."
136
+ puts "It might take a few minutes to gather all the info"
137
+ puts "so grab a crisp cool beverage, sit back and relax."
138
+ puts "I'll take it from here."
139
+
140
+ directory = user.ward.to_pdf( @options.marshal_dump )
141
+ directory.render_file(@options.output_file)
142
+
143
+ puts "\nFinished. Enjoy.\n"
144
+
145
+ rescue Wagon::AuthenticationFailure
146
+ puts "\nThe username and password combination you entered is invalid."
147
+ rescue
148
+ if @options.verbose
149
+ raise
150
+ else
151
+ puts "\nI encountered an unexpected problem, and I don't know what to do. :("
152
+ end
153
+ end
154
+ end
155
+
156
+ app = WagonApp.new(ARGV, STDIN)
157
+ app.run
data/cache/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.cache
Binary file
data/lib/wagon.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Wagon
2
+ BASE_PATH = File.join(File.dirname(__FILE__), '..')
3
+ VERSION = open(File.join(BASE_PATH, 'VERSION')).read()
4
+
5
+ def self.connect(username, password)
6
+ Connection.new(username, password)
7
+ end
8
+ end
9
+
10
+ require 'wagon/page'
11
+ require 'wagon/connection'
@@ -0,0 +1,35 @@
1
+ module Wagon
2
+ class Address
3
+ CITY_STATE_ZIP = %r/^(\D+), (\D+)?\s*(\d+(-\d+)?)?$/
4
+
5
+ attr_reader :street, :city, :state, :zip, :country
6
+
7
+ def self.extract_from_string(string)
8
+ parts = string.split("\n").collect(&:strip).delete_if(&:empty?)
9
+ street = city = state = zip = country = nil
10
+
11
+ parts.delete_if do |part|
12
+ next unless part =~ CITY_STATE_ZIP
13
+ city, state, zip = $1, ($2 || '').strip(), $3; true
14
+ end
15
+
16
+ self.new(parts.shift, city, state, zip, parts.shift)
17
+ end
18
+
19
+ def initialize(street, city, state, zip, country)
20
+ @street, @city, @state, @zip, @country = street, city, state, zip, country
21
+ end
22
+
23
+ def to_s
24
+ [street, [[city, state].compact.join(", "), zip, country.to_s.empty? ? nil : "(#{country})"].compact.join(" ")].compact.join("\n")
25
+ end
26
+
27
+ def ==(other)
28
+ street == other.street &&
29
+ city == other.city &&
30
+ state == other.state &&
31
+ zip == other.zip &&
32
+ country == other.country
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,81 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'digest/sha1'
5
+ require 'wagon/ward'
6
+
7
+ module Wagon
8
+
9
+ class AuthenticationFailure < StandardError; end
10
+
11
+ class Connection
12
+ HOST = 'secure.lds.org'
13
+ LOGIN_PATH = '/units/a/login/1,21568,779-1,00.html?URL='
14
+
15
+ def initialize(username, password)
16
+ response = post(LOGIN_PATH, 'username' => username, 'password' => password)
17
+ @cookies = response['set-cookie']
18
+ @home_path = URI.parse(response['location']).path
19
+
20
+ raise AuthenticationFailure.new("Invalid username and/or password") unless @cookies
21
+ end
22
+
23
+ def home_path
24
+ @home_path
25
+ end
26
+
27
+ def ward
28
+ @ward ||= Ward.new(self, home_path)
29
+ end
30
+
31
+ def get(path)
32
+ Connection.perform_caching? ? get_with_caching(path) : get_without_caching(path)
33
+ end
34
+
35
+ def get_without_caching(path)
36
+ _http.request(Net::HTTP::Get.new(path, {'Cookie' => @cookies || ''})).body
37
+ end
38
+
39
+ def get_with_caching(path)
40
+ cache_path = File.join(Wagon::BASE_PATH, 'cache', Digest::SHA1.hexdigest(path) + ".cache")
41
+ return open(cache_path).read if File.exists?(cache_path)
42
+ open(cache_path, "w").write(data = get_without_caching(path))
43
+ data
44
+ end
45
+
46
+ def self.perform_caching?
47
+ @@perform_caching ||= true
48
+ end
49
+
50
+ def self.perform_caching(true_or_false)
51
+ @@perform_caching = true_or_false
52
+ end
53
+
54
+ def post(path, data)
55
+ request = Net::HTTP::Post.new(path, {'Cookie' => @cookies || ''})
56
+ request.set_form_data(data)
57
+ _http.request(request)
58
+ end
59
+
60
+ def _dump(depth)
61
+ Marshal.dump([@cookies, @home_path])
62
+ end
63
+
64
+ def self._load(string)
65
+ attributes = Marshal.restore(string)
66
+ connection = Connection.allocate()
67
+ connection.instance_variable_set(:@cookies, attributes.shift)
68
+ connection.instance_variable_set(:@home_path, attributes.shift)
69
+ connection
70
+ end
71
+
72
+ private
73
+ def _http
74
+ return @http unless @http.nil?
75
+ @http = Net::HTTP.new(HOST, 443)
76
+ @http.use_ssl = true
77
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
78
+ @http
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,72 @@
1
+ require 'wagon/photo_directory'
2
+ require 'prawn'
3
+ require 'stringio'
4
+
5
+ module Wagon
6
+ class Directory < Page
7
+ def photo_directory_path
8
+ return @photo_directory_path unless @photo_directory_path.nil?
9
+
10
+ self.at('a.linknoline[href^="javascript:confirm_photo"]')['href'].match(/^javascript:confirm_photo\('(.*)'\);$/)
11
+ @photo_directory_path = $1
12
+ end
13
+
14
+ def photo_directory
15
+ @photo_directory ||= PhotoDirectory.new(connection, photo_directory_path)
16
+ end
17
+
18
+ def households
19
+ @households ||= photo_directory.households.sort
20
+ end
21
+
22
+ def members
23
+ households.collect(&:members).flatten()
24
+ end
25
+
26
+ def to_pdf(options = {})
27
+ options = {:columns => 7, :rows => 6, :padding => 2, :font_size => 8, :address => true, :phone_number => true, :email => true}.merge(options)
28
+
29
+ Prawn::Document.new(:skip_page_creation => true, :left_margin => 10, :right_margin => 10, :top_margin => 20, :bottom_margin => 10) do |pdf|
30
+ columns = options[:columns].to_f
31
+ rows = options[:rows].to_f
32
+ padding = options[:padding].to_f
33
+ grid_width = pdf.bounds.width / columns
34
+ grid_height = pdf.bounds.height / rows
35
+ box_width = grid_width - (padding * 2)
36
+ box_height = grid_height - (padding * 2)
37
+ pages = (households.size.to_f / (columns * rows)).ceil()
38
+ info_lines = 1 + [:address, :phone_number, :email].inject(0) { |sum, item| sum += item ? 1 : 0 }
39
+ pdf.font_size = options[:font_size].to_i
40
+
41
+ (0...pages).each do |page|
42
+ pdf.start_new_page
43
+ (0...rows).each do |row|
44
+ y = pdf.bounds.top - row*grid_height
45
+ (0...columns).each do |column|
46
+ break if (index = page*rows*columns+row*columns+column) >= households.size
47
+ household = households[index]
48
+ x = pdf.bounds.left + column*grid_width
49
+ pdf.bounding_box([x, y], :width => grid_width, :height => grid_height) do
50
+ pdf.bounding_box([pdf.bounds.left + padding, pdf.bounds.top - padding], :width => box_width, :height => box_height) do
51
+ information = []
52
+ information.push(household.name)
53
+ information.push(*household.address.street) if options[:address]
54
+ information.push(household.phone_number) if options[:phone_number]
55
+ information.push(household.members.first.email) if options[:email]
56
+
57
+ pdf.image(household.has_image? ? StringIO.new(household.image_data) : './extra/placeholder.jpg',
58
+ :position => :center, :fit => [box_width, box_height - (padding*2 + pdf.font.height*info_lines)] )
59
+
60
+ pdf.move_down(padding)
61
+ information.compact.each do |line|
62
+ pdf.text(line, :align => :center, :size => pdf.font_size.downto(1).detect() { |size| pdf.width_of(line.to_s, :size => size) <= box_width })
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,63 @@
1
+ require 'wagon/address'
2
+ require 'wagon/phone_number'
3
+ require 'wagon/member'
4
+
5
+ require 'base64'
6
+
7
+ module Wagon
8
+ class Household
9
+ attr_reader :connection
10
+ attr_writer :name
11
+ attr_accessor :address, :phone_number, :image_path, :members
12
+
13
+ def initialize(connection)
14
+ @members = []
15
+ @connection = connection
16
+ end
17
+
18
+ def name
19
+ self.individual? ? "#{members.first.name} #{@name}" : "#{@name} Household"
20
+ end
21
+
22
+ def reversed_name
23
+ self.individual? ? "#{@name}, #{members.first.name}" : name
24
+ end
25
+
26
+ def individual?
27
+ members.count == 1
28
+ end
29
+
30
+ def has_image?
31
+ !image_path.to_s.empty?
32
+ end
33
+
34
+ def image_data
35
+ @image_data ||= image_path.to_s.empty? ? "" : connection.get(image_path)
36
+ end
37
+
38
+ def <=>(other)
39
+ if has_image? == other.has_image?
40
+ reversed_name <=> other.reversed_name
41
+ else
42
+ has_image? ? -1 : 1
43
+ end
44
+ end
45
+
46
+ def self.create_from_td(connection, td)
47
+ Household.new(connection).instance_eval do
48
+ name_element, phone_element, *member_elements = *td.search('table > tr > td.eventsource[width="45%"] > table > tr > td.eventsource')
49
+ @address = Address.extract_from_string(td.search('table > tr > td.eventsource[width="25%"]').inner_text)
50
+ @image_path = td.at('table > tr > td.eventsource[width="30%"] > img')['src'] rescue nil
51
+ @name = name_element.inner_text
52
+ @phone_number = PhoneNumber.extract_from_string(phone_element.inner_text)
53
+
54
+ member_elements.each_slice(2) do |name_and_email|
55
+ name, email = *name_and_email.collect { |element| element.inner_text.gsub(/\302\240/, '').strip() }
56
+ @members << Member.new(self, name, email)
57
+ end
58
+
59
+ self
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,11 @@
1
+ class Member
2
+ attr_reader :household, :name, :email
3
+
4
+ def initialize(household, name, email)
5
+ @household, @name, @email = household, name, email
6
+ end
7
+
8
+ def to_s
9
+ [name, household.name, email.to_s.empty? ? nil : "<#{email}>"].compact.join(" ")
10
+ end
11
+ end
data/lib/wagon/page.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'nokogiri'
2
+
3
+ module Wagon
4
+ class Page
5
+ attr_reader :connection
6
+
7
+ def initialize(connection, url)
8
+ @connection, @url = connection, url
9
+ end
10
+
11
+ def source
12
+ @source ||= Nokogiri::HTML(get(@url))
13
+ end
14
+
15
+ def get(*args)
16
+ connection.get(*args)
17
+ end
18
+
19
+ def post(*args)
20
+ connection.post(*args)
21
+ end
22
+
23
+ def method_missing(method, *args, &block)
24
+ unless source.respond_to?(method)
25
+ super(method, *args, &block)
26
+ else
27
+ source.send(method, *args, &block)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ module Wagon
2
+ class PhoneNumber
3
+ attr_reader :type, :value
4
+
5
+ def self.extract_from_string(string)
6
+ string.strip =~ /([\)\(\+\s\-\d]+)(\((.*)\))?$/
7
+ self.new($3 || 'Home', $1.strip)
8
+ end
9
+
10
+ def initialize(type, value)
11
+ @type, @value = type, value
12
+ end
13
+
14
+ def ==(other)
15
+ type == other.type &&
16
+ value == other.value
17
+ end
18
+
19
+ def to_s
20
+ self.value
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ require 'wagon/household'
2
+
3
+ module Wagon
4
+ class PhotoDirectory < Page
5
+ def households
6
+ @households ||= _parse_households
7
+ end
8
+
9
+ def members
10
+ households.collect(&:members).flatten()
11
+ end
12
+
13
+ def to_pdf(options)
14
+ Prawn::Document.new() do |pdf|
15
+ households.each do |household|
16
+ pdf.text household.name
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+ def _parse_households
23
+ self.search('body > table > tr > td.eventsource[@width="25%"]').collect do |household_td|
24
+ household = Household.create_from_td(connection, household_td)
25
+ end
26
+ end
27
+ end
28
+ end
data/lib/wagon/ward.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'wagon/directory'
2
+
3
+ module Wagon
4
+ class Ward < Page
5
+ def name
6
+ @name ||= self.at('a.channeltitle[href^="/units/home"]').inner_text.strip
7
+ end
8
+
9
+ def directory_path
10
+ @directory_path ||= self.at('a.directory[href^="/units/a/directory"]')['href']
11
+ end
12
+
13
+ def directory
14
+ @directory ||= Directory.new(connection, directory_path)
15
+ end
16
+
17
+ def households
18
+ directory.households
19
+ end
20
+
21
+ def members
22
+ households.collect(&:members).flatten()
23
+ end
24
+
25
+ def to_pdf(options)
26
+ directory.to_pdf(options)
27
+ end
28
+ end
29
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,35 @@
1
+ BASE_PATH = File.join(File.dirname(__FILE__), '..')
2
+ USER_FILE = File.join(BASE_PATH, 'spec', 'user.dat')
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(BASE_PATH, 'lib'))
6
+
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+ require 'rubygems'
10
+ require 'wagon'
11
+ require 'highline/import'
12
+
13
+ $user = nil
14
+
15
+ def establish_connection
16
+ username = ask("What is your lds.org username? ")
17
+ password = ask("What is your lds.org password? ") { |prompt| prompt.echo = "*" }
18
+
19
+ $user = Wagon::connect(username, password)
20
+ open(USER_FILE, 'w').write(Marshal.dump($user))
21
+ end
22
+
23
+ def restore_connection
24
+ $user = Marshal.restore(open(USER_FILE).read)
25
+ end
26
+
27
+ if File.exists?(USER_FILE)
28
+ restore_connection()
29
+ else
30
+ establish_connection()
31
+ end
32
+
33
+ Spec::Runner.configure do |config|
34
+
35
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe "Wagon::Directory" do
4
+
5
+ before(:each) do
6
+ @page = Wagon::Directory.new($user, $user.ward.directory_path)
7
+ end
8
+
9
+ it "should find the photo directory link" do
10
+ @page.photo_directory_path.should_not be_nil
11
+ @page.photo_directory_path.should match(%r{^/units/a/directory/photoprint})
12
+ end
13
+
14
+ it "should be able to generate a pdf" do
15
+ @page.to_pdf.render_file("./photo_directory.pdf")
16
+ end
17
+
18
+ end
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe "Wagon::PhotoDirectory" do
4
+
5
+ before(:each) do
6
+ @page = Wagon::PhotoDirectory.new($user, $user.ward.directory.photo_directory_path)
7
+ end
8
+
9
+ it "should parse out the households correctly" do
10
+ @page.households.should have_at_least(10).items
11
+ end
12
+
13
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe "Wagon::Ward" do
4
+
5
+ before(:each) do
6
+ @page = Wagon::Ward.new($user, $user.home_path)
7
+ end
8
+
9
+ it "should have a name" do
10
+ @page.name.to_s.should_not be_empty
11
+ end
12
+
13
+ it "should find the directory link" do
14
+ @page.directory_path.should_not be_nil
15
+ @page.directory_path.should match(%r{^/units/a/directory})
16
+ end
17
+
18
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Wagon" do
4
+
5
+ before(:each) do
6
+ @user = $user
7
+ end
8
+
9
+ it "should be connected" do
10
+ @user.should_not be_nil
11
+ @user.home_path.should_not be_nil
12
+ end
13
+
14
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wagon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Devin Christensen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-07 00:00:00 -07:00
13
+ default_executable: wagon
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: nokogiri
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.4.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: highline
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: prawn
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.5.1
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: rspec
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.9
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ description: Provided a valid lds.org username and password, Wagon will download all the information from the Photo Directory page and compile it into a convenient PDF.
66
+ email: devin@threetrieslater.com
67
+ executables:
68
+ - wagon
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - LICENSE
73
+ - README.rdoc
74
+ files:
75
+ - .document
76
+ - .gitignore
77
+ - LICENSE
78
+ - README.rdoc
79
+ - Rakefile
80
+ - VERSION
81
+ - bin/wagon
82
+ - cache/.gitignore
83
+ - extra/placeholder.jpg
84
+ - lib/wagon.rb
85
+ - lib/wagon/address.rb
86
+ - lib/wagon/connection.rb
87
+ - lib/wagon/directory.rb
88
+ - lib/wagon/household.rb
89
+ - lib/wagon/member.rb
90
+ - lib/wagon/page.rb
91
+ - lib/wagon/phone_number.rb
92
+ - lib/wagon/photo_directory.rb
93
+ - lib/wagon/ward.rb
94
+ - spec/spec.opts
95
+ - spec/spec_helper.rb
96
+ - spec/wagon/directory_spec.rb
97
+ - spec/wagon/photo_directory_spec.rb
98
+ - spec/wagon/ward_spec.rb
99
+ - spec/wagon_spec.rb
100
+ has_rdoc: true
101
+ homepage: http://github.com/threetrieslater/wagon
102
+ licenses: []
103
+
104
+ post_install_message:
105
+ rdoc_options:
106
+ - --charset=UTF-8
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: "0"
114
+ version:
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: "0"
120
+ version:
121
+ requirements: []
122
+
123
+ rubyforge_project:
124
+ rubygems_version: 1.3.5
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Create a PDF from the lds.org ward Photo Directory.
128
+ test_files:
129
+ - spec/spec_helper.rb
130
+ - spec/wagon/directory_spec.rb
131
+ - spec/wagon/photo_directory_spec.rb
132
+ - spec/wagon/ward_spec.rb
133
+ - spec/wagon_spec.rb