govkit-h 0.7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/.document +5 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +20 -0
  5. data/README.md +70 -0
  6. data/Rakefile +82 -0
  7. data/USAGE +1 -0
  8. data/VERSION +1 -0
  9. data/generators/govkit/govkit_generator.rb +24 -0
  10. data/generators/govkit/templates/govkit.rb +24 -0
  11. data/govkit.gemspec +130 -0
  12. data/init.rb +4 -0
  13. data/lib/generators/govkit/govkit_generator.rb +21 -0
  14. data/lib/generators/govkit/templates/create_mentions.rb +21 -0
  15. data/lib/generators/govkit/templates/govkit.rb +24 -0
  16. data/lib/generators/govkit/templates/mention.rb +15 -0
  17. data/lib/gov_kit.rb +45 -0
  18. data/lib/gov_kit/acts_as_noteworthy.rb +63 -0
  19. data/lib/gov_kit/configuration.rb +58 -0
  20. data/lib/gov_kit/follow_the_money.rb +176 -0
  21. data/lib/gov_kit/open_congress.rb +125 -0
  22. data/lib/gov_kit/open_congress/bill.rb +171 -0
  23. data/lib/gov_kit/open_congress/blog_post.rb +15 -0
  24. data/lib/gov_kit/open_congress/news_post.rb +15 -0
  25. data/lib/gov_kit/open_congress/person.rb +141 -0
  26. data/lib/gov_kit/open_congress/person_stat.rb +13 -0
  27. data/lib/gov_kit/open_congress/roll_call.rb +14 -0
  28. data/lib/gov_kit/open_congress/roll_call_comparison.rb +28 -0
  29. data/lib/gov_kit/open_congress/voting_comparison.rb +44 -0
  30. data/lib/gov_kit/open_states.rb +132 -0
  31. data/lib/gov_kit/railtie.rb +24 -0
  32. data/lib/gov_kit/resource.rb +190 -0
  33. data/lib/gov_kit/search_engines.rb +7 -0
  34. data/lib/gov_kit/search_engines/bing.rb +38 -0
  35. data/lib/gov_kit/search_engines/google_blog.rb +32 -0
  36. data/lib/gov_kit/search_engines/google_news.rb +47 -0
  37. data/lib/gov_kit/search_engines/technorati.rb +35 -0
  38. data/lib/gov_kit/search_engines/wikipedia.rb +27 -0
  39. data/lib/gov_kit/transparency_data.rb +144 -0
  40. data/lib/gov_kit/vote_smart.rb +126 -0
  41. data/lib/govkit.rb +1 -0
  42. data/spec/fixtures/bing/news_search.response +1 -0
  43. data/spec/fixtures/bing/no_results.response +1 -0
  44. data/spec/fixtures/follow_the_money/business-page0.response +28 -0
  45. data/spec/fixtures/follow_the_money/business-page1.response +12 -0
  46. data/spec/fixtures/follow_the_money/contribution.response +12 -0
  47. data/spec/fixtures/follow_the_money/unauthorized.response +8 -0
  48. data/spec/fixtures/open_congress/person.response +8 -0
  49. data/spec/fixtures/open_states/401.response +10 -0
  50. data/spec/fixtures/open_states/404.response +9 -0
  51. data/spec/fixtures/open_states/410.response +6 -0
  52. data/spec/fixtures/open_states/bill.response +240 -0
  53. data/spec/fixtures/open_states/bill_query.response +1990 -0
  54. data/spec/fixtures/open_states/committee_find.response +53 -0
  55. data/spec/fixtures/open_states/committee_query.response +190 -0
  56. data/spec/fixtures/open_states/legislator.response +34 -0
  57. data/spec/fixtures/open_states/legislator_query.response +144 -0
  58. data/spec/fixtures/open_states/state.response +60 -0
  59. data/spec/fixtures/search_engines/google_news.response +8 -0
  60. data/spec/fixtures/transparency_data/contributions.response +18 -0
  61. data/spec/fixtures/transparency_data/entities_search.response +7 -0
  62. data/spec/fixtures/transparency_data/entities_search_limit_0.response +7 -0
  63. data/spec/fixtures/transparency_data/entities_search_limit_1.response +7 -0
  64. data/spec/fixtures/transparency_data/grants_find_all.response +7 -0
  65. data/spec/fixtures/transparency_data/lobbyists_find_all.response +7 -0
  66. data/spec/follow_the_money_spec.rb +61 -0
  67. data/spec/open_congress_spec.rb +108 -0
  68. data/spec/open_states_spec.rb +213 -0
  69. data/spec/search_engines_spec.rb +44 -0
  70. data/spec/spec_helper.rb +36 -0
  71. data/spec/transparency_data_spec.rb +106 -0
  72. metadata +258 -0
@@ -0,0 +1,15 @@
1
+ # A model to contain mentions of the :owner in the media
2
+ class Mention < ActiveRecord::Base
3
+ belongs_to :owner, :polymorphic => true
4
+
5
+ scope :since, lambda { |d| where(["mentions.date > ?", d]) }
6
+
7
+ # Returns the mentions in JSON form
8
+ #
9
+ # @params [Hash] A hash of options
10
+ # @return The mentions in JSON form
11
+ def as_json(opts = {})
12
+ default_opts = {:except => [:owner_id, :owner_type]}
13
+ super(default_opts.merge(opts))
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'digest/md5'
4
+ require 'active_support'
5
+ require 'nokogiri'
6
+ require 'iconv'
7
+ require 'httparty'
8
+ require 'open-uri'
9
+ require 'json'
10
+ require 'gov_kit/configuration'
11
+ require 'csv'
12
+
13
+ if RUBY_VERSION[0,3] == "1.8"
14
+ require 'fastercsv'
15
+ end
16
+
17
+ module GovKit
18
+ autoload :Resource, 'gov_kit/resource'
19
+ autoload :OpenStates, 'gov_kit/open_states'
20
+ autoload :TransparencyData, 'gov_kit/transparency_data'
21
+ autoload :VoteSmart, 'gov_kit/vote_smart'
22
+ autoload :ActsAsNoteworthy, 'gov_kit/acts_as_noteworthy'
23
+ autoload :FollowTheMoney, 'gov_kit/follow_the_money'
24
+ autoload :OpenCongress, 'gov_kit/open_congress'
25
+ autoload :SearchEngines, 'gov_kit/search_engines'
26
+
27
+ # Convenience class to represent a news story or blog post.
28
+ # Used by GovKit::SearchEngines classes.
29
+ class Mention
30
+ attr_accessor :url, :excerpt, :title, :source, :date, :weight, :search_source
31
+ end
32
+
33
+ class GovKitError < StandardError
34
+ end
35
+
36
+ class NotAuthorized < GovKitError; end
37
+
38
+ class InvalidRequest < GovKitError; end
39
+
40
+ class ResourceNotFound < GovKitError; end
41
+
42
+ class ServerError < GovKitError; end
43
+
44
+ class ClientError < GovKitError; end
45
+ end
@@ -0,0 +1,63 @@
1
+ module GovKit::ActsAsNoteworthy
2
+
3
+ def self.included(base)
4
+ base.extend ActMethods
5
+ end
6
+
7
+ # Module to make a rails model act as a noteworthy object, linking it to govkit's mention model
8
+ module ActMethods
9
+ # Sets up the relationship between the model and the mention model
10
+ #
11
+ # @param [Hash] opts a hash of options to be used by the relationship
12
+ def acts_as_noteworthy(options={})
13
+ class_inheritable_accessor :options
14
+ self.options = options
15
+
16
+ unless included_modules.include? InstanceMethods
17
+ instance_eval do
18
+ has_many :mentions, :as => :owner, :order => 'date desc'
19
+
20
+ with_options :as => :owner, :class_name => "Mention" do |c|
21
+ c.has_many :google_news_mentions, :conditions => {:search_source => "Google News"}, :order => 'date desc'
22
+ c.has_many :google_blog_mentions, :conditions => {:search_source => "Google Blogs"}, :order => 'date desc'
23
+ # c.has_many :technorati_mentions, :conditions => {:search_source => "Technorati"}, :order => 'date desc'
24
+ c.has_many :bing_mentions, :conditions => {:search_source => "Bing"}, :order => 'date desc'
25
+ end
26
+ end
27
+
28
+ extend ClassMethods
29
+ include InstanceMethods
30
+ end
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+ end
36
+
37
+ # A module that adds methods to individual instances of the noteworthy class.
38
+ module InstanceMethods
39
+ # Generates the raw mentions to be loaded into the Mention objects
40
+ #
41
+ # @return [Hash] a hash of all the mentions found of the object in question.
42
+ def raw_mentions
43
+ opts = self.options.clone
44
+ attributes = opts.delete(:with)
45
+
46
+ if opts[:geo]
47
+ opts[:geo] = self.instance_eval("#{opts[:geo]}")
48
+ end
49
+
50
+ query = []
51
+ attributes.each do |attr|
52
+ query << self.instance_eval("#{attr}")
53
+ end
54
+
55
+ {
56
+ :google_news => GovKit::SearchEngines::GoogleNews.search(query, opts),
57
+ :google_blogs => GovKit::SearchEngines::GoogleBlog.search(query, opts),
58
+ # :technorati => GovKit::SearchEngines::Technorati.search(query),
59
+ :bing => GovKit::SearchEngines::Bing.search(query, opts)
60
+ }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,58 @@
1
+ module GovKit
2
+ class Configuration
3
+ attr_accessor :sunlight_apikey, :openstates_base_url, :transparency_data_base_url, :transparency_data_categories_url
4
+ attr_accessor :votesmart_apikey, :votesmart_base_url
5
+ attr_accessor :openstates_apikey, :ftm_apikey, :ftm_base_url
6
+ attr_accessor :opencongress_apikey, :opencongress_base_url
7
+ attr_accessor :technorati_apikey, :technorati_base_url
8
+ attr_accessor :google_blog_base_url, :google_news_base_url
9
+ attr_accessor :wikipedia_base_url
10
+ attr_accessor :bing_appid, :bing_base_url
11
+
12
+ def initialize
13
+ @sunlight_apikey = @openstates_apikey = @votesmart_apikey = @ftm_apikey = ''
14
+ @openstates_base_url = 'openstates.sunlightlabs.com/api/v1/'
15
+ @transparency_data_base_url = 'transparencydata.com/api/1.0/'
16
+ @votesmart_base_url = 'api.votesmart.org/'
17
+ @ftm_base_url = 'api.followthemoney.org/'
18
+ @opencongress_base_url = 'www.opencongress.org/'
19
+ @technorati_base_url = 'api.technorati.com'
20
+ @bing_base_url = 'api.search.live.net'
21
+ @google_blog_base_url = 'blogsearch.google.com'
22
+ @google_news_base_url = 'news.google.com'
23
+ @wikipedia_base_url = 'en.wikipedia.org'
24
+
25
+ # Permant home for contribution category mappings
26
+ @transparency_data_categories_url = 'http://assets.transparencydata.org.s3.amazonaws.com/docs/catcodes.csv'
27
+ end
28
+
29
+ def opencongress_apikey= key
30
+ warn "[DEPRECATION] OpenCongress no longer requires an API Key. Ability to set it will be removed in future versions"
31
+ @opencongress_apikey = key
32
+ end
33
+
34
+ def openstates_apikey= key
35
+ warn "[DEPRECATION] Use sunlight_apikey instead of openstates_apikey. Ability to set it will be removed in future versions"
36
+ @sunlight_apikey = key
37
+ end
38
+ end
39
+
40
+ class << self
41
+ attr_accessor :configuration
42
+
43
+ def configuration
44
+ @configuration = Configuration.new if @configuration.nil?
45
+ @configuration
46
+ end
47
+ end
48
+
49
+ # Configure GovKit in config/initializers/govkit.rb
50
+ #
51
+ # @example
52
+ # GovKit.configure do |config|
53
+ # config.sunlight_apikey = ''
54
+ # end
55
+ def self.configure
56
+ yield(configuration)
57
+ end
58
+ end
@@ -0,0 +1,176 @@
1
+ module GovKit
2
+
3
+ # Subclass of {Resource} for FollowTheMoney data. This
4
+ # is subclassed further for each of the different types of record
5
+ # returned by FollowTheMoney.
6
+ #
7
+ # For the details on the FollowTheMoney queries, see {http://www.followthemoney.org/services/methods.phtml the FollowTheMoney API documentation}.
8
+ class FollowTheMoneyResource < Resource
9
+ default_params :key => GovKit::configuration.ftm_apikey
10
+ base_uri GovKit::configuration.ftm_base_url
11
+
12
+ # Common method used by subclasses to get data from the service.
13
+ #
14
+ # @return [Nokogiri::XML::Document] a {http://nokogiri.org/Nokogiri/HTML/Document.html Nokogiri XML::Document} object
15
+ # @param [String] path query path that specifies the required data
16
+ # @param [Hash] options query options
17
+ #
18
+ # @example
19
+ # doc = get_xml("/base_level.industries.list.php", :query => {:page => page_num})
20
+ #
21
+ def self.get_xml(path, options)
22
+ doc = Nokogiri::XML(get(path, options))
23
+
24
+ e = doc.search("//error")
25
+
26
+ # Deal with whatever error comes back
27
+ if e.size > 0
28
+ raise case e.first.attributes['code'].value
29
+ when "100"
30
+ GovKit::NotAuthorized
31
+ when "300"
32
+ GovKit::InvalidRequest
33
+ when "200"
34
+ GovKit::ResourceNotFound
35
+ else
36
+ GovKit::InvalidRequest
37
+ end, e.first.attributes['text'].value
38
+ end
39
+
40
+ doc
41
+ end
42
+
43
+ # Convert the hash array returned by Nokogiri, which has Nokogiri::XML::Attr objects as values,
44
+ # to an array of hashes with string values.
45
+ #
46
+ # @param [Array] result array of hashes, with object values.
47
+ # @return [Array] array of hashes, with string values.
48
+ def self.stringify_values_of(result)
49
+ result.collect! { |r| r.inject({}) {|h, (k, v)| h[k] = v.to_s; h } }
50
+ end
51
+ end
52
+
53
+ # Provides classes to wrap {http://www.followthemoney.org/index.phtml FollowTheMoney} data.
54
+ #
55
+ # For the details on the FollowTheMoney queries, see {http://www.followthemoney.org/services/methods.phtml the FollowTheMoney API documentation}.
56
+ module FollowTheMoney
57
+
58
+ # Wrap {http://www.followthemoney.org/services/method_doc.phtml?a=11 Industry data}
59
+ class Business < FollowTheMoneyResource
60
+
61
+ # Return a list of all business, industry, and sector categories. See the {http://www.followthemoney.org/services/method_doc.phtml?a=11 FollowTheMoney API}.
62
+ #
63
+ # @return [Business] A list of Business objects.
64
+ def self.list
65
+ next_page, result, page_num = "yes", [], 0
66
+
67
+ until next_page != "yes"
68
+ # puts "Getting batch number #{page_num}"
69
+
70
+ doc = get_xml("/base_level.industries.list.php", :query => {:page => page_num})
71
+
72
+ next_page = doc.children.first.attributes['next_page'].value
73
+
74
+ page_num += 1
75
+
76
+ result += doc.search('//business_detail').collect { |x| x.attributes }
77
+ end
78
+
79
+ stringify_values_of(result)
80
+ parse(result)
81
+ end
82
+ end
83
+
84
+ # Wrap contributions to a candidate. See the {http://www.followthemoney.org/services/method_doc.phtml?a=32 FollowTheMoney API}.
85
+ class Contribution < FollowTheMoneyResource
86
+
87
+ # Return contributions to a candidate. See the {http://www.followthemoney.org/services/method_doc.phtml?a=32 FollowTheMoney API}.
88
+ #
89
+ # @param [Integer] nimsp_id the candidate id.
90
+ #
91
+ # @return [Contribution] a Contribution object, or array of Contribution objects, representing the contributions.
92
+ def self.find(nimsp_id)
93
+ next_page, result, page_num = "yes", [], 0
94
+
95
+ until next_page != "yes"
96
+ doc = get_xml("/candidates.contributions.php", :query => {"imsp_candidate_id" => nimsp_id, :page => page_num})
97
+
98
+ next_page = doc.children.first.attributes['next_page'].value
99
+
100
+ page_num += 1
101
+
102
+ result += doc.search('//contribution').collect { |x| x.attributes }
103
+ end
104
+
105
+ stringify_values_of(result)
106
+ parse(result)
107
+ end
108
+
109
+ # Return a list of the top contributors to a candidate. See the {http://www.followthemoney.org/services/method_doc.phtml?a=20 FollowTheMoney API}.
110
+ #
111
+ # @param [Integer] nimsp_id the candidate id.
112
+ #
113
+ # @return [[Contribution]] an array of Contribution objects.
114
+ def self.top(nimsp_id)
115
+ doc = get_xml("/candidates.top_contributor.php", :query => {"imsp_candidate_id" => nimsp_id})
116
+ result = doc.search('//top_contributor').collect { |x| x.attributes }
117
+
118
+ stringify_values_of(result)
119
+ parse(result)
120
+ end
121
+ end
122
+
123
+ # Wrap contributions by industry to a candidate. See the {http://www.followthemoney.org/services/method_doc.phtml?a=24 FollowTheMoney API}.
124
+ #
125
+ class IndustryContribution < Contribution
126
+ # Return contributions by industry.
127
+ #
128
+ # @param [Integer] nimsp_id the candidate id.
129
+ #
130
+ # @return [[Contribution]] an array of Contribution objects.
131
+ def self.find(nimsp_id)
132
+ doc = get_xml("/candidates.industries.php", :query => {"imsp_candidate_id" => nimsp_id})
133
+ result = doc.search('//candidate_industry').collect { |x| x.attributes }
134
+
135
+ stringify_values_of(result)
136
+ parse(result)
137
+ end
138
+ end
139
+
140
+ # Wrap contributions by sector to a candidate. See the {http://www.followthemoney.org/services/method_doc.phtml?a=23 FollowTheMoney API}.
141
+ #
142
+ class SectorContribution < Contribution
143
+ # Return conributions by sector.
144
+ #
145
+ # @param [Integer] nimsp_id the candidate id.
146
+ #
147
+ # @return [[Contribution]] an array of Contribution objects.
148
+ def self.find(nimsp_id)
149
+ doc = get_xml("/candidates.sectors.php", :query => {"imsp_candidate_id" => nimsp_id})
150
+
151
+ result = doc.search('//candidate_sector').collect { |x| x.attributes }
152
+
153
+ stringify_values_of(result)
154
+ parse(result)
155
+ end
156
+ end
157
+
158
+ # Wrap contributions by business to a candidate. See the {http://www.followthemoney.org/services/method_doc.phtml?a=25 FollowTheMoney API}.
159
+ #
160
+ class BusinessContribution < Contribution
161
+ # Return contributions by business.
162
+ #
163
+ # @param [Integer] nimsp_id the candidate id.
164
+ #
165
+ # @return [[Contribution]] an array of Contribution objects.
166
+ def self.find(nimsp_id)
167
+ doc = get_xml("/candidates.businesses.php", :query => {"imsp_candidate_id" => nimsp_id})
168
+
169
+ result = doc.search('//candidate_business').collect { |x| x.attributes }
170
+
171
+ stringify_values_of(result)
172
+ parse(result)
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,125 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+ require 'json'
4
+ require 'CGI'
5
+
6
+ module GovKit::OpenCongress
7
+ autoload :Bill, 'gov_kit/open_congress/bill'
8
+ autoload :BlogPost, 'gov_kit/open_congress/blog_post'
9
+ autoload :NewsPost, 'gov_kit/open_congress/news_post'
10
+ autoload :VotingComparison, 'gov_kit/open_congress/voting_comparison'
11
+ autoload :RollCallComparison, 'gov_kit/open_congress/roll_call_comparison'
12
+ autoload :Person, 'gov_kit/open_congress/person'
13
+ autoload :PersonStat, 'gov_kit/open_congress/person_stat'
14
+
15
+ # Parent class for classes that wrap {http://www.opencongress.org/api OpenCongress data}.
16
+ #
17
+ # Unlike the wrapper classes for data from {FollowTheMoneyResource FollowTheMoney},
18
+ # {OpenStatesResource OpenStates}, {TransparencyDataResource TransparencyData},
19
+ # and {VoteSmartResource VoteSmart}, OpenCongressObject does not inherit
20
+ # from {GovKit::Resource}
21
+ #
22
+ class OpenCongressObject
23
+
24
+ def initialize(obj, params)
25
+ params.each do |key, value|
26
+ key = key.to_sym if RUBY_VERSION[0,3] == "1.9"
27
+ instance_variable_set("@#{key}", value) if obj.instance_methods.include? key
28
+ end
29
+ end
30
+
31
+ # Create a query url, by adding the method path and query parameters to the
32
+ # base url of {http://www.opencongress.org/api}.
33
+ def self.construct_url(api_method, params)
34
+ url = nil
35
+ getkey = GovKit::configuration.opencongress_apikey.nil? ? "" : "&key=#{GovKit::configuration.opencongress_apikey}"
36
+ url = "http://#{GovKit::configuration.opencongress_base_url}api/#{api_method}?format=json#{hash2get(params)}#{getkey}"
37
+ return url
38
+ end
39
+
40
+ # Convert a hash to a string of query parameters.
41
+ #
42
+ # @param [Hash] h a hash.
43
+ # @return [String] a string of query parameters.
44
+ def self.hash2get(h)
45
+ get_string = ""
46
+
47
+ h.each_pair do |key, value|
48
+ get_string += "&#{key.to_s}=#{CGI::escape(value.to_s)}"
49
+ end
50
+
51
+ get_string
52
+ end
53
+
54
+ # Iterates through the array returned by {make_call},
55
+ # converting each hash to an OpenCongressObject subclass.
56
+ #
57
+ # @param [Hash] results the array returned by make_call.
58
+ # @return a hash of arrays of OpenCongressObject objects, with these keys:
59
+ # * :also_supporting_bills
60
+ # * :also_opposing_bills
61
+ # * :also_disapproved_senators
62
+ # * :also_disapproved_representatives
63
+ # * :also_approved_senators
64
+ # * :also_approved_representatives
65
+ def self.parse_supporting_results(result)
66
+ working = result["opencongress_users_tracking"]
67
+
68
+ also_supporting_bills = []
69
+ working["also_supporting_bills"]["bill"].each do |bill|
70
+ also_supporting_bills << Bill.new(bill)
71
+ end
72
+
73
+ also_opposing_bills = []
74
+ working["also_opposing_bills"]["bill"].each do |bill|
75
+ also_opposing_bills << Bill.new(bill)
76
+ end
77
+
78
+ also_disapproved_senators = []
79
+ working["also_disapproved_senators"]["person"].each do |person|
80
+ also_disapproved_senators << Person.new(person)
81
+ end
82
+
83
+ also_disapproved_representatives = []
84
+ working["also_disapproved_representatives"]["person"].each do |person|
85
+ also_disapproved_representatives << Person.new(person)
86
+ end
87
+
88
+ also_approved_senators = []
89
+ working["also_approved_senators"]["person"].each do |person|
90
+ also_approved_senators << Person.new(person)
91
+ end
92
+
93
+ also_approved_representatives = []
94
+ working["also_approved_representatives"]["person"].each do |person|
95
+ also_approved_representatives << Person.new(person)
96
+ end
97
+
98
+ return {:also_supporting_bills => also_supporting_bills,
99
+ :also_opposing_bills => also_opposing_bills,
100
+ :also_disapproved_senators => also_disapproved_senators,
101
+ :also_disapproved_representatives => also_disapproved_representatives,
102
+ :also_approved_senators => also_approved_senators,
103
+ :also_approved_representatives => also_approved_representatives}
104
+
105
+ end
106
+
107
+ # Get the data from {http://www.opencongress.org/api OpenCongress data}. Called by subclasses.
108
+ #
109
+ # Parses the data using {http://flori.github.com/json/doc/index.html JSON.parse}, which
110
+ # returns an array of hashes.
111
+ #
112
+ # @return the returned data, as an array of hashes.
113
+ def self.make_call(this_url)
114
+ result = nil
115
+ begin
116
+ result = JSON.parse(open(this_url).read)
117
+ rescue => e
118
+ puts e
119
+ end
120
+
121
+ return result
122
+
123
+ end
124
+ end
125
+ end