get_your_rep 0.1.9 → 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.
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ module GetYourRep
3
+ # Office information. Belongs to Representative.
4
+ class OfficeLocation
5
+ include GetYourRep::Errors
6
+
7
+ # Each instance belongs to a Represenative, which has many OfficeLocations.
8
+ attr_accessor :rep
9
+ # Defines the type of office ('district' or 'capitol')
10
+ attr_accessor :type
11
+ # Line 1 of address.
12
+ attr_accessor :line_1
13
+ # Line 2 of address.
14
+ attr_accessor :line_2
15
+ # City, state and zip.
16
+ attr_accessor :city, :state, :zip
17
+
18
+ # Construct a new instance, setting attributes from an options hash.
19
+ def initialize(office_hash = {})
20
+ office_hash.each do |key, val|
21
+ send("#{key}=", val)
22
+ end
23
+ end
24
+
25
+ # Set the rep attribute to an instance of a Representative, and make two-way association.
26
+ def rep=(other)
27
+ if other.is_a?(Representative)
28
+ @rep = other
29
+ other.add_office_location(self) unless other.office_locations.include?(self)
30
+ else
31
+ not_a_rep_error
32
+ end
33
+ end
34
+
35
+ # Display self attributes for CLI.
36
+ def cli_display
37
+ puts " #{type.capitalize} Office".bold.blue
38
+ office_lines = [line_1, line_2, "#{city}, #{state} #{zip}"]
39
+ office_lines.each { |line| puts " #{line}".red if line }
40
+ end
41
+ end
42
+ end
@@ -1,114 +1,56 @@
1
+ # frozen_string_literal: true
1
2
  module GetYourRep
2
-
3
3
  # Retrieve your elected state representatives from the Open States API, parse it from
4
4
  # JSON, and assemble it into a usable Ruby Hash-like object called a Representative.
5
5
  # Representatives are then wrapped in an Array-like object called a Delegation.
6
- class OpenStates
7
-
8
- # Initiates a chain of class method calls that will instantiate a
9
- # Delegation object and return it.
10
- def self.now(address)
11
- @coordinates = if address.is_a?(Array)
12
- address
13
- else
14
- get_coordinates(address)
15
- end
16
- @response = get_rep
17
- if @response.empty? || @response.is_a?(String) || @response.first['error']
18
- puts 'Error message received. Confirm and re-enter your address and check your parameters.'
19
- puts @response
20
- return GetYourRep::Delegation.new
21
- end
22
-
23
- parse_rep
24
- @delegation
25
- end
26
-
27
- # Geocodes an address and returns the coordinates.
28
- def self.get_coordinates(address)
29
- return address if address.is_a?(Array)
30
- address = '0%o' % address if address.is_a?(Integer)
31
- raise "Entry must be coordinates or of types String or Integer" if !address.is_a?(String)
32
- Geocoder.coordinates(address)
33
- end
34
-
35
- # Sets parameters for and executes Open States API request.
36
- def self.get_rep
37
- if @coordinates
38
- lat = @coordinates.first
39
- long = @coordinates.last
40
- url = "http://openstates.org/api/v1/legislators/geo/?lat=#{lat}&long=#{long}"
41
- HTTParty.get(url).parsed_response
42
- else
43
- []
44
- end
45
- end
46
-
47
- # Parses the JSON response and assembles it into a Delegation object.
48
- def self.parse_rep
49
-
50
- @delegation = GetYourRep::Delegation.new
51
-
52
- @response.each do |rep|
53
- @delegation << GetYourRep::Representative[
54
- :name, rep['full_name'].split(', ').reverse.join(' '),
55
- :office, "#{rep['state'].upcase} #{rep['chamber'].capitalize} Chamber, #{rep['district']}",
56
- :party, rep['party'],
57
- :phone, phone(rep),
58
- :office_locations, offices(rep),
59
- :email, email(rep),
60
- :url, rep['url'],
61
- :photo, rep['photo_url'],
62
- :twitter, nil,
63
- :facebook, nil,
64
- :youtube, nil,
65
- :googleplus, nil,
66
- :committees, committees(rep)
67
- ]
68
- end
69
- end
70
-
71
- def self.offices(rep)
72
- rep['offices'].map do |office|
73
-
74
- # TODO change office parsing to RegEx
75
- office_hash = {}
76
- office_hash[:type] = office['type']
77
- address = office['address'].split("\n")
78
-
79
- if address.size == 1
80
- address = office['address'].split(', ')
6
+ module OpenStates
7
+ class << self
8
+ include GetYourRep
9
+
10
+ # Holds the coordinates geocoded from the address. Used in the HTTP request to the API.
11
+ attr_accessor :coordinates
12
+ # Holds the Delegation object which will be returned by the all_reps class method.
13
+ attr_accessor :delegation
14
+ # Holds the raw JSON data response from the API.
15
+ attr_accessor :response
16
+
17
+ # Initiates a chain of class method calls that will instantiate a
18
+ # Delegation object and return it.
19
+ def all_reps(address)
20
+ self.coordinates = address.is_a?(Array) ? address : get_coordinates(address)
21
+ self.response = find_rep
22
+ if response.empty? || response.is_a?(String) || response.first['error']
23
+ handle_reps_not_found_error
24
+ else
25
+ parse_reps
26
+ delegation
81
27
  end
28
+ end
82
29
 
83
- if office['name'].downcase != "capitol office" &&
84
- office['name'].downcase != "district office"
85
-
86
- address.unshift(office['name'])
30
+ private
31
+
32
+ # Sets parameters for and executes Open States API request.
33
+ def find_rep
34
+ if coordinates
35
+ lat = coordinates.first
36
+ long = coordinates.last
37
+ url = "http://openstates.org/api/v1/legislators/geo/?lat=#{lat}&long=#{long}"
38
+ HTTParty.get(url).parsed_response
39
+ else
40
+ Delegation.new
87
41
  end
42
+ end
88
43
 
89
- i = 1
90
- address.each do |line|
91
- office_hash["line_#{i}".to_sym] = line
92
- i += 1
44
+ # Parses the JSON response and assembles it into a Delegation object.
45
+ def parse_reps
46
+ self.delegation = Delegation.new
47
+ response.each do |rep|
48
+ external_rep = OpenStatesRep.new(rep)
49
+ rep_hash = external_rep.build_hash
50
+ new_rep = Representative.new(rep_hash)
51
+ delegation.add_rep(new_rep)
93
52
  end
94
-
95
- office_hash
96
53
  end
97
54
  end
98
-
99
- # Parses the phone, giving preference to district office..
100
- def self.phone(rep)
101
- rep['offices'].map { |office| office['phone']} - [nil]
102
- end
103
-
104
- # Parses the email address.
105
- def self.email(rep)
106
- rep['offices'].map { |office| office['email'] } - [nil]
107
- end
108
-
109
- # Parses committee involvement.
110
- def self.committees(rep)
111
- rep['roles'].map { |role| role['committee'] } - [nil]
112
- end
113
55
  end
114
56
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+ module GetYourRep
3
+ # Red, white and blue decorations for CLI.
4
+ module Patriotic
5
+ # Stars banner.
6
+ def self.banner
7
+ puts ' ' + '_' * 23 + ' '.bold.red
8
+ puts "\u23B9".bold.red + ' * * * * * * * * * * * '.white.on_blue + "\u23B8".bold.red
9
+ puts ' ' + "\u203E".bold.red * 23 + ' '
10
+ end
11
+
12
+ # Stars and Bars
13
+ def self.stars_and_bars
14
+ 4.times { puts stars_and_red_bar, stars_and_white_bar }
15
+ puts stars_and_red_bar
16
+ 3.times { puts white_bar, red_bar }
17
+ end
18
+
19
+ # Stars and red bar.
20
+ def self.stars_and_red_bar
21
+ ' * * * * * * '.white.on_blue + ' '.on_red
22
+ end
23
+
24
+ # Stars and white bar.
25
+ def self.stars_and_white_bar
26
+ ' * * * * * '.white.on_blue + ' '.on_white
27
+ end
28
+
29
+ # Red bar.
30
+ def self.red_bar
31
+ ' '.on_red
32
+ end
33
+
34
+ # White bar.
35
+ def self.white_bar
36
+ ' '.on_white
37
+ end
38
+
39
+ # Fireworks welcome message.
40
+ def self.welcome
41
+ puts ''
42
+ 2.times do
43
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e '.red + 'y o u r '.white + 'r e p '.blue +
44
+ '- '.red + '- '.white + '- '.blue
45
+ sleep(0.05)
46
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + 'y o r '.white + 'r e p '.blue +
47
+ '- '.red + '- '.white + '- '.blue
48
+ sleep(0.05)
49
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + 'y o u r '.white + 'r p '.blue +
50
+ '- '.red + '- '.white + '- '.blue
51
+ sleep(0.05)
52
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + 'y o u '.white + 'r e p '.blue +
53
+ '- '.red + '- '.white + '- '.blue
54
+ sleep(0.05)
55
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + ' o u r '.white + 'r e p '.blue +
56
+ '- '.red + '- '.white + '- '.blue
57
+ sleep(0.05)
58
+ print "\r" + '- '.red + '- '.white + '- '.blue + ' e t '.red + 'y o u r '.white + 'r e p '.blue +
59
+ '- '.red + '- '.white + '- '.blue
60
+ sleep(0.05)
61
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + 'y o u r '.white + 'r e '.blue +
62
+ '- '.red + '- '.white + '- '.blue
63
+ sleep(0.05)
64
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + 'y o u r '.white + 'r e p '.blue +
65
+ '- '.red + '- '.white + '- '.blue
66
+ sleep(0.05)
67
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + 'y u r '.white + 'r e p '.blue +
68
+ '- '.red + '- '.white + '- '.blue
69
+ sleep(0.05)
70
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g t '.red + 'y o u r '.white + 'r e p '.blue +
71
+ '- '.red + '- '.white + '- '.blue
72
+ sleep(0.05)
73
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + 'y o u r '.white + ' e p '.blue +
74
+ '- '.red + '- '.white + '- '.blue
75
+ sleep(0.05)
76
+ print "\r" + '- '.red + '- '.white + '- '.blue + 'g e t '.red + 'y o u r '.white + 'r e p '.blue +
77
+ '- '.red + '- '.white + '- '.blue
78
+ end
79
+ puts ''
80
+ end
81
+ end
82
+ end
@@ -1,294 +1,128 @@
1
+ # frozen_string_literal: true
1
2
  module GetYourRep
2
-
3
3
  # Stores rep info in key/value pairs, and makes values accessible by instance method.
4
- class Representative < Hash
5
-
6
- # Maps attributes to a simple array for easy printing, iteration, and display. It uses #each rather than #map so it can skip over nil values without mapping them.
7
- def business_card
8
-
9
- card = []
10
-
11
- self.each do |key, value|
12
- next if value.nil?
13
- if key == :facebook || key == :twitter || key == :youtube || key == :googleplus
14
- card << "#{key.to_s.capitalize}: #{value}"
15
- else
16
- card << "#{value}"
17
- end
4
+ class Representative
5
+ include GetYourRep::Errors
6
+
7
+ # The Delegation object that the instance belongs to.
8
+ attr_reader :delegation
9
+ # Rep personal attributes.
10
+ attr_accessor :name, :middle_name, :office, :phones, :party, :email, :committees, :url,
11
+ :photo, :twitter, :facebook, :youtube, :googleplus
12
+
13
+ # Set office_locations, phones, and email as empty arrays. Set the rest of the attributes
14
+ # from the options hash.
15
+ def initialize(rep_hash = {})
16
+ @office_locations = []
17
+ @phones = []
18
+ @email = []
19
+ rep_hash.each do |key, val|
20
+ send("#{key}=", val)
18
21
  end
19
-
20
- card
21
- end
22
-
23
- # Get the :name value.
24
- def name
25
- @name = self[:name]
26
- end
27
-
28
- # Set the :name value.
29
- def name=(value)
30
- @name = value
31
- self[:name] = @name
32
22
  end
33
23
 
34
- # Strips the first name out of the full name if there's no :first_name.
35
- def first_name
36
-
37
- return self[:first_name] if self[:first_name]
38
-
39
- if name.split.count == 1
40
- nil
41
- elsif (name.split.count > 3) || (name.split[-2].downcase == name.split[-2])
42
- name.split[0..-3].join(' ')
24
+ # Sets the Delegation object that the instance belongs to. Will add self to the Delegation's
25
+ # reps if not done so already.
26
+ def delegation=(other)
27
+ if other.is_a?(Delegation)
28
+ @delegation = other
29
+ other.add_rep(self) unless other.reps.include?(self)
43
30
  else
44
- name.split[0..-2].join(' ')
31
+ not_a_del_error
45
32
  end
46
-
47
- end
48
-
49
- # Get the :middle_name value.
50
- def middle_name
51
- @middle_name = self[:middle_name]
52
- end
53
-
54
- # Strips the surname out of the full name if there's no :last_name.
55
- def last_name
56
-
57
- return self[:last_name] if self[:last_name]
58
-
59
- if name.split.count == 1
60
- name
61
- elsif (name.split.count > 3) || (name.split[-2].downcase == name.split[-2])
62
- name.split[-2..-1].join(' ')
63
- else
64
- name.split.last
65
- end
66
-
67
- end
68
-
69
- # Get the :office value.
70
- def office
71
- @office = self[:office]
72
- end
73
-
74
- # Set the :office value.
75
- def office=(value)
76
- @office = value
77
- self[:office] = @office
78
- end
79
-
80
- # Get the :party value.
81
- def party
82
- @party = self[:party]
83
- end
84
-
85
- # Set the :party value.
86
- def party=(value)
87
- @party = value
88
- self[:party] = @party
89
- end
90
-
91
- # Get the :phone value.
92
- def phone
93
- @phone = self[:phone]
94
33
  end
95
34
 
96
- # Set the :phone value.
97
- def phone=(value)
98
- @phone = value
99
- self[:phone] = @phone
100
- end
101
-
102
- # Get the :alt_phone value.
103
- # def alt_phone
104
- # @alt_phone = self[:alt_phone]
105
- # end
106
-
107
- # Set the :alt_phone value.
108
- # def alt_phone=(value)
109
- # @alt_phone = value
110
- # self[:alt_phone] = @alt_phone
111
- # end
112
-
113
- # Get the :alt_address_1 value.
114
- # def alt_address_1
115
- # @alt_address_1 = self[:alt_address_1]
116
- # end
117
-
118
- # Set the :alt_address_1 value.
119
- # def alt_address_1=(value)
120
- # @alt_address_1 = value
121
- # self[:alt_address_1] = @alt_address_1
122
- # end
123
-
124
- # Get the :alt_address_2 value.
125
- # def alt_address_2
126
- # @alt_address_2 = self[:alt_address_2]
127
- # end
128
-
129
- # Set the :alt_address_2 value.
130
- # def alt_address_2=(value)
131
- # @alt_address_2 = value
132
- # self[:alt_address_2] = @alt_address_2
133
- # end
134
-
135
- # Get the :alt_address_3 value.
136
- # def alt_address_3
137
- # @alt_address_3 = self[:alt_address_3]
138
- # end
139
-
140
- # Set the :alt_address_3 value.
141
- # def alt_address_3=(value)
142
- # @alt_address_3 = value
143
- # self[:alt_address_3] = @alt_address_3
144
- # end
145
-
146
- # Get the :address_1 value.
147
- # def address_1
148
- # @address_1 = self[:address_1]
149
- # end
150
-
151
- # Set the :address_1 value.
152
- # def address_1=(value)
153
- # @address_1 = value
154
- # self[:address_1] = @address_1
155
- # end
156
-
157
- # Get the :address_2 value.
158
- # def address_2
159
- # @address_2 = self[:address_2]
160
- # end
161
-
162
- # Set the :address_2 value.
163
- # def address_2=(value)
164
- # @address_2 = value
165
- # self[:address_2] = @address_2
166
- # end
167
-
168
- # Get the :address_3 value.
169
- # def address_3
170
- # @address_3 = self[:address_3]
171
- # end
172
-
173
- # Set the :address_3 value.
174
- # def address_3=(value)
175
- # @address_3 = value
176
- # self[:address_3] = @address_3
177
- # end
178
-
35
+ # Returns a frozen duplicate of the office_locations array.
179
36
  def office_locations
180
- @office_locations = self[:office_locations]
37
+ @office_locations.dup.freeze
181
38
  end
182
39
 
183
- def office_locations=(value)
184
- @office_locations = value
185
- self[:office_locations] = @office_locations
40
+ # Empties the office_locations array.
41
+ def clear_office_locations
42
+ @office_locations.clear
186
43
  end
187
44
 
188
- def district_office
189
- @district_office = self.office_locations.select { |loc| loc[:type] == 'district' }.first
190
- if @district_office.nil?
191
- {}
45
+ # Creates a new OfficeLocation association. Sets self as the other's rep if not done so already.
46
+ def add_office_location(other)
47
+ if other.is_a?(OfficeLocation)
48
+ @office_locations << other
49
+ other.rep = self unless other.rep == self
192
50
  else
193
- @district_office
51
+ not_an_office_error
194
52
  end
195
53
  end
196
54
 
197
- def capitol_office
198
- @capitol_office = self.office_locations.select { |loc| loc[:type] == 'capitol' }.first
199
- if @capitol_office.nil?
200
- {}
55
+ # Assign an individual OfficeLocation, or an array of them.
56
+ def office_locations=(other)
57
+ if other.is_a?(Array)
58
+ other.each { |val| add_office_location(val) }
201
59
  else
202
- @capitol_office
60
+ add_office_location(other)
203
61
  end
204
62
  end
205
63
 
206
- # Get the :email value.
207
- def email
208
- @email = self[:email]
209
- end
210
-
211
- # Set the :email value.
212
- def email=(value)
213
- @email = value
214
- self[:email] = @email
215
- end
216
-
217
- # Get the :committees value.
218
- def committees
219
- @committees = self[:committees]
220
- end
221
-
222
- # Set the :committees value.
223
- def committees=(value)
224
- @committees = value
225
- self[:committees] = @committees
226
- end
227
-
228
- # Get the :url value.
229
- def url
230
- @url = self[:url]
231
- end
232
-
233
- # Set the :url value.
234
- def url=(value)
235
- @url = value
236
- self[:url] = @url
237
- end
238
-
239
- # Get the :photo value.
240
- def photo
241
- @photo = self[:photo]
242
- end
243
-
244
- # Set the :photo value.
245
- def photo=(value)
246
- @photo = value
247
- self[:photo] = @photo
64
+ # Parse the first name from the :name.
65
+ def first_name
66
+ @first_name ||= if name_count == 1
67
+ nil
68
+ elsif (name_count > 3) || (name_array[-2].downcase == name_array[-2])
69
+ name_array[0..-3].join(' ')
70
+ else
71
+ name_array[0..-2].join(' ')
72
+ end
248
73
  end
249
74
 
250
- # Get the :twitter value.
251
- def twitter
252
- @twitter = self[:twitter]
75
+ # Parse the last name from the :name.
76
+ def last_name
77
+ @last_name ||= if name_count == 1
78
+ name
79
+ elsif (name_count > 3) || (name_array[-2].downcase == name_array[-2])
80
+ name_array[-2..-1].join(' ')
81
+ else
82
+ name_array.last
83
+ end
253
84
  end
254
85
 
255
- # Set the :twitter value.
256
- def twitter=(value)
257
- @twitter = value
258
- self[:twitter] = @twitter
86
+ # Splits the name into an array.
87
+ def name_array
88
+ @name_array ||= name.split
259
89
  end
260
90
 
261
- # Get the :facebook value.
262
- def facebook
263
- @facebook = self[:facebook]
91
+ # Counts the elements in the name array.
92
+ def name_count
93
+ @name_count ||= name_array.size
264
94
  end
265
95
 
266
- # Set the :facebook value.
267
- def facebook=(value)
268
- @facebook = value
269
- self[:facebook] = @facebook
96
+ # Maps the offices with the :type attribute equal to 'district'.
97
+ def district_offices
98
+ @district_offices ||= office_locations.select { |office| office.type == 'district' }
270
99
  end
271
100
 
272
- # Get the :youtube value.
273
- def youtube
274
- @youtube = self[:youtube]
101
+ # Maps the offices with the :type attribute equal to 'capitol'.
102
+ def capitol_offices
103
+ @district_offices ||= office_locations.select { |office| office.type == 'capitol' }
275
104
  end
276
105
 
277
- # Set the :youtube value.
278
- def youtube=(value)
279
- @youtube = value
280
- self[:youtube] = @youtube
106
+ # Displays self for the CLI.
107
+ def cli_display
108
+ cli_basic_info
109
+ office_locations.each(&:cli_display)
110
+ cli_display_committees
281
111
  end
282
112
 
283
- # Get the :googleplus value.
284
- def googleplus
285
- @googleplus = self[:googleplus]
113
+ # Display basic info.
114
+ def cli_basic_info
115
+ puts name.bold.blue
116
+ puts " #{office}".red
117
+ puts " #{party}".red
118
+ phones.each { |phone| puts " #{phone}".red }
286
119
  end
287
120
 
288
- # Set the :googleplus value.
289
- def googleplus=(value)
290
- @googleplus = value
291
- self[:googleplus] = @googleplus
121
+ # Display committee info.
122
+ def cli_display_committees
123
+ return unless committees && !committees.empty?
124
+ puts ' Committees'.bold.blue
125
+ committees.each { |comm| puts " #{comm}".red }
292
126
  end
293
127
  end
294
128
  end