wagon 0.10.5 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/bin/wagon +145 -145
- data/gui/Rakefile +5 -0
- data/gui/build.xml +74 -0
- data/gui/build/classes/.netbeans_automatic_build +0 -0
- data/gui/build/classes/org/rubyforge/rawr/Main.class +0 -0
- data/gui/build_configuration.rb +31 -0
- data/gui/icons/monkeybars.icns +0 -0
- data/gui/icons/monkeybars.ico +0 -0
- data/gui/lib/java/jruby-complete.jar +0 -0
- data/gui/lib/java/monkeybars-1.0.4.jar +0 -0
- data/gui/lib/ruby/README.txt +1 -0
- data/gui/manifest.mf +3 -0
- data/gui/nbproject/build-impl.xml +794 -0
- data/gui/nbproject/genfiles.properties +8 -0
- data/gui/nbproject/private/private.properties +3 -0
- data/gui/nbproject/project.properties +67 -0
- data/gui/nbproject/project.xml +13 -0
- data/gui/src/application_controller.rb +4 -0
- data/gui/src/application_view.rb +3 -0
- data/gui/src/main.rb +52 -0
- data/gui/src/manifest.rb +58 -0
- data/gui/src/org/rubyforge/rawr/Main.java +67 -0
- data/gui/src/resolver.rb +33 -0
- data/gui/tasks/monkeybars.rake +89 -0
- data/lib/wagon.rb +12 -10
- data/lib/wagon/address.rb +39 -39
- data/lib/wagon/connection.rb +73 -107
- data/lib/wagon/directory.rb +78 -78
- data/lib/wagon/household.rb +59 -74
- data/lib/wagon/member.rb +10 -10
- data/lib/wagon/page.rb +30 -31
- data/lib/wagon/phone_number.rb +22 -22
- data/lib/wagon/ward.rb +28 -28
- data/spec/wagon/directory_spec.rb +22 -22
- data/spec/wagon/ward_spec.rb +18 -18
- metadata +35 -2
data/lib/wagon/connection.rb
CHANGED
@@ -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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
data/lib/wagon/directory.rb
CHANGED
@@ -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
|
data/lib/wagon/household.rb
CHANGED
@@ -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
|
-
@
|
16
|
-
@
|
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
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|