wagon 0.10.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,108 +1,74 @@
1
- require 'net/http'
2
- require 'net/https'
3
- require 'uri'
4
- require 'digest/sha1'
5
- require 'wagon/ward'
6
-
7
- module Wagon
8
- class AuthenticationFailure < StandardError; end
9
-
10
- class Connection
11
- HOST = 'secure.lds.org'
12
- LOGIN_PATH = '/units/a/login/1,21568,779-1,00.html?URL='
13
-
14
- # For asynchronous procedures
15
- @@trigger = ConditionVariable.new
16
- @@lock = Mutex.new
17
- @@queue = []
18
-
19
- (1..30).collect do
20
- Thread.new do
21
- http = Net::HTTP.new(HOST, 443)
22
- http.use_ssl = true
23
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
24
- http
25
-
26
- while true
27
- connection, path, callback = nil, nil, nil
28
- @@lock.synchronize do
29
- connection, path, callback = *@@queue.shift
30
- end
31
-
32
- if connection
33
- callback.call(http.request(Net::HTTP::Get.new(path, {'Cookie' => connection.cookies || ''})))
34
- else
35
- sleep(0.5)
36
- end
37
- end
38
- end
39
- end
40
-
41
- attr_reader :cookies
42
-
43
- def initialize(username, password)
44
- response = _post(LOGIN_PATH, 'username' => username, 'password' => password)
45
- @cookies = response['set-cookie']
46
- @home_path = URI.parse(response['location']).path
47
-
48
- raise AuthenticationFailure.new("Invalid username and/or password") unless @cookies
49
- end
50
-
51
- def home_path
52
- @home_path
53
- end
54
-
55
- def ward
56
- @ward ||= Ward.new(self, home_path)
57
- end
58
-
59
- def get(path)
60
- _get(path).body
61
- end
62
-
63
- def get_async(path, &block)
64
- @@lock.synchronize do
65
- @@queue.push([self, path, block])
66
- end
67
- end
68
-
69
- def expired?
70
- _head(ward.directory_path).class != Net::HTTPOK
71
- end
72
-
73
- def _dump(depth)
74
- Marshal.dump([@cookies, @home_path])
75
- end
76
-
77
- def self._load(string)
78
- attributes = Marshal.restore(string)
79
- connection = Connection.allocate()
80
- connection.instance_variable_set(:@cookies, attributes.shift)
81
- connection.instance_variable_set(:@home_path, attributes.shift)
82
- connection
83
- end
84
-
85
- private
86
- def _http
87
- return @http unless @http.nil?
88
- @http = Net::HTTP.new(HOST, 443)
89
- @http.use_ssl = true
90
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
91
- @http
92
- end
93
-
94
- def _get(path)
95
- _http.request(Net::HTTP::Get.new(path, {'Cookie' => @cookies || ''}))
96
- end
97
-
98
- def _head(path)
99
- _http.request(Net::HTTP::Head.new(path, {'Cookie' => @cookies || ''}))
100
- end
101
-
102
- def _post(path, data)
103
- request = Net::HTTP::Post.new(path, {'Cookie' => @cookies || ''})
104
- request.set_form_data(data)
105
- _http.request(request)
106
- end
107
- end
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'digest/sha1'
5
+ require 'wagon/ward'
6
+
7
+ module Wagon
8
+ class AuthenticationFailure < StandardError; end
9
+
10
+ class Connection
11
+ HOST = 'secure.lds.org'
12
+ LOGIN_PATH = '/units/a/login/1,21568,779-1,00.html?URL='
13
+
14
+ attr_reader :cookies
15
+
16
+ def initialize(username, password)
17
+ response = _post(LOGIN_PATH, 'username' => username, 'password' => password)
18
+ @cookies = response['set-cookie']
19
+ @home_path = URI.parse(response['location']).path
20
+
21
+ raise AuthenticationFailure.new("Invalid username and/or password") unless @cookies
22
+ end
23
+
24
+ def home_path
25
+ @home_path
26
+ end
27
+
28
+ def ward
29
+ @ward ||= Ward.new(self, home_path)
30
+ end
31
+
32
+ def get(path)
33
+ _get(path).body
34
+ end
35
+
36
+ def expired?
37
+ _head(ward.directory_path).class != Net::HTTPOK
38
+ end
39
+
40
+ def _dump(depth)
41
+ Marshal.dump([@cookies, @home_path])
42
+ end
43
+
44
+ def self._load(string)
45
+ attributes = Marshal.restore(string)
46
+ connection = Connection.allocate()
47
+ connection.instance_variable_set(:@cookies, attributes.shift)
48
+ connection.instance_variable_set(:@home_path, attributes.shift)
49
+ connection
50
+ end
51
+
52
+ private
53
+ def _http
54
+ http = Net::HTTP.new(HOST, 443)
55
+ http.use_ssl = true
56
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
57
+ http
58
+ end
59
+
60
+ def _get(path)
61
+ _http.request(Net::HTTP::Get.new(path, {'Cookie' => @cookies || ''}))
62
+ end
63
+
64
+ def _head(path)
65
+ _http.request(Net::HTTP::Head.new(path, {'Cookie' => @cookies || ''}))
66
+ end
67
+
68
+ def _post(path, data)
69
+ request = Net::HTTP::Post.new(path, {'Cookie' => @cookies || ''})
70
+ request.set_form_data(data)
71
+ _http.request(request)
72
+ end
73
+ end
108
74
  end
@@ -1,78 +1,78 @@
1
- require 'wagon/household'
2
- require 'prawn'
3
- require 'stringio'
4
-
5
- module Wagon
6
- class Directory < Page
7
- def ward
8
- @parent
9
- end
10
-
11
- def households
12
- @households ||= self.search('body > table > tr > td.eventsource[@width="25%"]').collect do |household_td|
13
- household = Household.create_from_td(connection, household_td)
14
- end.sort
15
- end
16
-
17
- def to_pdf(options = {})
18
- options = {
19
- :columns => 7,
20
- :rows => 6,
21
- :padding => 2,
22
- :font_size => 8,
23
- :address => true,
24
- :phone_number => true,
25
- :email => true,
26
- :title => "#{ward.name}"
27
- }.merge(options.delete_if {|k,v| v.nil? })
28
-
29
- Prawn::Document.new(:left_margin => 10, :right_margin => 10, :top_margin => 10, :bottom_margin => 10) do |pdf|
30
- header_height = 10
31
- footer_height = 10
32
- columns = options[:columns].to_f
33
- rows = options[:rows].to_f
34
- padding = options[:padding].to_f
35
- grid_width = pdf.bounds.width / columns
36
- grid_height = (pdf.bounds.height - header_height - footer_height) / rows
37
- box_width = grid_width - (padding * 2)
38
- box_height = grid_height - (padding * 2)
39
- pages = (households.size.to_f / (columns * rows)).ceil()
40
- pdf.font_size = options[:font_size].to_i
41
- info_count = 1 + [:address, :phone_number, :email].inject(0) { |sum, item| sum += options[item] ? 1 : 0 }
42
- info_height = pdf.font.height*info_count
43
-
44
- (0...pages).each do |page|
45
- pdf.start_new_page unless page == 0
46
- pdf.text(options[:title], :at => [pdf.bounds.right/2 - pdf.width_of(options[:title], :size => 12)/2, pdf.bounds.top - header_height + padding], :size => 12)
47
- pdf.text("For Church Use Only", :at => [pdf.bounds.right/2 - pdf.width_of("For Church Use Only")/2, pdf.bounds.bottom])
48
- (0...rows).each do |row|
49
- y = pdf.bounds.top - row*grid_height - header_height
50
- (0...columns).each do |column|
51
- break if (index = page*rows*columns+row*columns+column) >= households.size
52
- x = pdf.bounds.left + column*grid_width
53
- household = households[index]
54
-
55
- pdf.bounding_box([x, y], :width => grid_width, :height => grid_height) do
56
- pdf.bounding_box([pdf.bounds.left + padding, pdf.bounds.top - padding], :width => box_width, :height => box_height) do
57
- info = []
58
- info.push(household.name)
59
- info.push(*household.address.street) if options[:address]
60
- info.push(household.phone_number.value) if options[:phone_number]
61
- info.push(household.members.first.email) if options[:email]
62
-
63
- pdf.image(household.has_image? ? StringIO.new(household.image_data) : File.join(Wagon::BASE_PATH, 'extra', 'placeholder.jpg'), :position => :center, :fit => [box_width, box_height - (padding*2 + info_height)] )
64
-
65
- pdf.bounding_box([pdf.bounds.left, pdf.bounds.bottom + info_height], :height => info_height+1, :width => pdf.bounds.width) do
66
- info.compact.each do |line|
67
- pdf.text(line, :align => :center, :size => pdf.font_size.downto(1).detect() { |size| pdf.width_of(line.to_s, :size => size) <= box_width })
68
- end
69
- end
70
- end
71
- end
72
- end
73
- end
74
- end
75
- end
76
- end
77
- end
78
- end
1
+ require 'wagon/household'
2
+ require 'prawn'
3
+ require 'stringio'
4
+
5
+ module Wagon
6
+ class Directory < Page
7
+ def ward
8
+ @parent
9
+ end
10
+
11
+ def households
12
+ @households ||= self.search('body > table > tr > td.eventsource[@width="25%"]').collect do |household_td|
13
+ household = Household.create_from_td(connection, household_td)
14
+ end.sort
15
+ end
16
+
17
+ def to_pdf(options = {})
18
+ options = {
19
+ :columns => 7,
20
+ :rows => 6,
21
+ :padding => 2,
22
+ :font_size => 8,
23
+ :address => true,
24
+ :phone_number => true,
25
+ :email => true,
26
+ :title => "#{ward.name}"
27
+ }.merge(options.delete_if {|k,v| v.nil? })
28
+
29
+ Prawn::Document.new(:left_margin => 10, :right_margin => 10, :top_margin => 10, :bottom_margin => 10) do |pdf|
30
+ header_height = 10
31
+ footer_height = 10
32
+ columns = options[:columns].to_f
33
+ rows = options[:rows].to_f
34
+ padding = options[:padding].to_f
35
+ grid_width = pdf.bounds.width / columns
36
+ grid_height = (pdf.bounds.height - header_height - footer_height) / rows
37
+ box_width = grid_width - (padding * 2)
38
+ box_height = grid_height - (padding * 2)
39
+ pages = (households.size.to_f / (columns * rows)).ceil()
40
+ pdf.font_size = options[:font_size].to_i
41
+ info_count = 1 + [:address, :phone_number, :email].inject(0) { |sum, item| sum += options[item] ? 1 : 0 }
42
+ info_height = pdf.font.height*info_count
43
+
44
+ (0...pages).each do |page|
45
+ pdf.start_new_page unless page == 0
46
+ pdf.text(options[:title], :at => [pdf.bounds.right/2 - pdf.width_of(options[:title], :size => 12)/2, pdf.bounds.top - header_height + padding], :size => 12)
47
+ pdf.text("For Church Use Only", :at => [pdf.bounds.right/2 - pdf.width_of("For Church Use Only")/2, pdf.bounds.bottom])
48
+ (0...rows).each do |row|
49
+ y = pdf.bounds.top - row*grid_height - header_height
50
+ (0...columns).each do |column|
51
+ break if (index = page*rows*columns+row*columns+column) >= households.size
52
+ x = pdf.bounds.left + column*grid_width
53
+ household = households[index]
54
+
55
+ pdf.bounding_box([x, y], :width => grid_width, :height => grid_height) do
56
+ pdf.bounding_box([pdf.bounds.left + padding, pdf.bounds.top - padding], :width => box_width, :height => box_height) do
57
+ info = []
58
+ info.push(household.name)
59
+ info.push(*household.address.street) if options[:address]
60
+ info.push(household.phone_number.value) if options[:phone_number]
61
+ info.push(household.members.first.email) if options[:email]
62
+
63
+ pdf.image(household.has_image? ? StringIO.new(household.image_data) : File.join(Wagon::BASE_PATH, 'extra', 'placeholder.jpg'), :position => :center, :fit => [box_width, box_height - (padding*2 + info_height)] )
64
+
65
+ pdf.bounding_box([pdf.bounds.left, pdf.bounds.bottom + info_height], :height => info_height+1, :width => pdf.bounds.width) do
66
+ info.compact.each do |line|
67
+ pdf.text(line, :align => :center, :size => pdf.font_size.downto(1).detect() { |size| pdf.width_of(line.to_s, :size => size) <= box_width })
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,75 +1,60 @@
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, :address, :phone_number, :image_path, :members
10
-
11
- def initialize(connection, name, address, phone_number, image_path, members)
12
- @connection, @name, @address, @phone_number, @image_path, @members = connection, name, address, phone_number, image_path, members
13
-
14
- if has_image?
15
- @connection.get_async(image_path) do |response|
16
- @image_data = response.body
17
- end
18
- end
19
- end
20
-
21
- def name
22
- self.individual? ? "#{members.first.name} #{@name}" : "#{@name} Household"
23
- end
24
-
25
- def reversed_name
26
- self.individual? ? "#{@name}, #{members.first.name}" : name
27
- end
28
-
29
- def individual?
30
- members.count == 1
31
- end
32
-
33
- def has_image?
34
- !image_path.to_s.empty?
35
- end
36
-
37
- def image_data
38
- return nil unless has_image?
39
-
40
- sleep(0.5) while @image_data.nil?
41
-
42
- @image_data
43
- end
44
-
45
- def <=>(other)
46
- if has_image? == other.has_image?
47
- reversed_name <=> other.reversed_name
48
- else
49
- has_image? ? -1 : 1
50
- end
51
- end
52
-
53
- private
54
- def spawn_download_thread
55
- @thread ||= Thread.new(image_path) do |path|
56
- @image_data = connection.get_async(path)
57
- end
58
- end
59
-
60
- def self.create_from_td(connection, td)
61
- name_element, phone_element, *member_elements = *td.search('table > tr > td.eventsource[width="45%"] > table > tr > td.eventsource')
62
- address = Address.extract_from_string(td.search('table > tr > td.eventsource[width="25%"]').inner_text)
63
- image_path = td.at('table > tr > td.eventsource[width="30%"] > img')['src'] rescue nil
64
- phone_number = PhoneNumber.extract_from_string(phone_element.inner_text)
65
- members = []
66
-
67
- member_elements.each_slice(2) do |name_and_email|
68
- name, email = *name_and_email.collect { |element| element.inner_text.gsub(/\302\240/, '').strip() }
69
- members << Member.new(self, name, email)
70
- end
71
-
72
- self.new(connection, name_element.inner_text, address, phone_number, image_path, members)
73
- end
74
- end
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, :address, :phone_number, :image_path, :image_data, :members
10
+
11
+ def initialize(connection, name, address, phone_number, image_path, members)
12
+ @connection, @name, @address, @phone_number, @image_path, @members = connection, name, address, phone_number, image_path, members
13
+
14
+ if has_image?
15
+ @image_data = Future(image_path) do |image_path|
16
+ @connection.get(image_path)
17
+ end
18
+ end
19
+ end
20
+
21
+ def name
22
+ self.individual? ? "#{members.first.name} #{@name}" : "#{@name} Household"
23
+ end
24
+
25
+ def reversed_name
26
+ self.individual? ? "#{@name}, #{members.first.name}" : name
27
+ end
28
+
29
+ def individual?
30
+ members.count == 1
31
+ end
32
+
33
+ def has_image?
34
+ !image_path.to_s.empty?
35
+ end
36
+
37
+ def <=>(other)
38
+ if has_image? == other.has_image?
39
+ reversed_name <=> other.reversed_name
40
+ else
41
+ has_image? ? -1 : 1
42
+ end
43
+ end
44
+
45
+ def self.create_from_td(connection, td)
46
+ name_element, phone_element, *member_elements = *td.search('table > tr > td.eventsource[width="45%"] > table > tr > td.eventsource')
47
+ address = Address.extract_from_string(td.search('table > tr > td.eventsource[width="25%"]').inner_text)
48
+ image_path = td.at('table > tr > td.eventsource[width="30%"] > img')['src'] rescue nil
49
+ phone_number = PhoneNumber.extract_from_string(phone_element.inner_text)
50
+ members = []
51
+
52
+ member_elements.each_slice(2) do |name_and_email|
53
+ name, email = *name_and_email.collect { |element| element.inner_text.gsub(/\302\240/, '').strip() }
54
+ members << Member.new(self, name, email)
55
+ end
56
+
57
+ self.new(connection, name_element.inner_text, address, phone_number, image_path, members)
58
+ end
59
+ end
75
60
  end