wagon 0.10.5 → 1.0.0

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.
@@ -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