opengraph_transporter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: aa0283f8d7638544d60cda64f99807201bb1a5e5
4
+ data.tar.gz: 8fcd12be282e33dbdb62fef653c3ee621b4df58c
5
+ SHA512:
6
+ metadata.gz: d7dee52e5207f8e2ffcd78a56c4885373d7bc5d4bfd3d1d0e06caf97e3a93c6e4a12f5a70aa41ecb7309fd28e7b83fbca7c96b09cdbd004a9bed43b4d19c85d9
7
+ data.tar.gz: 8fd34a80d0b543ad24e37c5a33a6e3d779698a5953fe0fdc3dfb479b7b72bd17b8472a4a8880c9f480d788af067a953a1827c013c629307a54ed564c779b724d
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in opengraph_transporter.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Barry Quigley
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # OpengraphTransporter
2
+
3
+ opengraph_transporter is a Ruby console app that exports Facebook OpenGraph translations between Developer Apps.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'opengraph_transporter'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install opengraph_transporter
18
+
19
+
20
+ ## Requirements
21
+
22
+ * Ruby 1.9.3 or higher
23
+ * Firefox 22.0.0 or higher
24
+
25
+ ## Usage
26
+
27
+ 1. Run opengraph_transporter
28
+ 2. Enter Source Facebook Developer App ID and App Secret
29
+ 3. Enter Destination Facebook Developer App ID and App Secret
30
+ 4. Enter App translations locale.
31
+ 5. Follow the prompts.
32
+
33
+
34
+ ```
35
+ $ Please Enter Source Application Id
36
+ ```
37
+
38
+
39
+ ## Contributing
40
+
41
+ 1. Fork it
42
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
43
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
44
+ 4. Push to the branch (`git push origin my-new-feature`)
45
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'lib/opengraph_transporter'
8
+ t.test_files = FileList['test/lib/opengraph_transporter/*_test.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## $VERBOSE
4
+ ## -W0 NO Warnings nil
5
+ ## -W1 Quiet false
6
+ ## -W2 Verbose true
7
+ BEGIN { $VERBOSE = false }
8
+
9
+ begin
10
+ require 'opengraph_transporter'
11
+ rescue LoadError
12
+ require 'rubygems'
13
+ require 'opengraph_transporter'
14
+ end
15
+
16
+ # -----------------------------------------------------------------------
17
+ # Main
18
+ # -----------------------------------------------------------------------
19
+
20
+ OpengraphTransporter::Base.run
21
+
data/lib/.DS_Store ADDED
Binary file
Binary file
@@ -0,0 +1,214 @@
1
+ module OpengraphTransporter
2
+ class Base
3
+
4
+ class << self
5
+
6
+ @@translation = nil
7
+ @@user = {}
8
+
9
+ def run
10
+ setup
11
+ say("<%= color('Preparing Translations....', YELLOW, BOLD) %>")
12
+ prepare
13
+ Common.show_translations_info(translation)
14
+ say("<%= color('Process existing Destination application Translations (#{Base.translation[:dst_app_name]} native stubs) ....', YELLOW, BOLD) %>")
15
+ run_export
16
+ end
17
+
18
+ def translation
19
+ @@translation
20
+ end
21
+
22
+ def translation=(value)
23
+ @@translation = value
24
+ end
25
+
26
+ def user
27
+ @@user
28
+ end
29
+
30
+ def user=(value)
31
+ @@user = value
32
+ end
33
+
34
+ def inspect
35
+ "<#{self.name} user: #{user} translation: #{translation.inspect} >"
36
+ end
37
+
38
+ def to_s
39
+ inspect
40
+ end
41
+
42
+ private
43
+
44
+ def setup
45
+ display_app_splash
46
+ get_apps_details
47
+ self
48
+ end
49
+
50
+ def run_export
51
+ translation[:dst_translation_arr] = Scraper.ingest_app_translations(translation[:destination_application_id], translation[:app_locale])
52
+ if translation[:dst_translation_arr].length == 0
53
+ say("No destination app <%= color('(#{Base.translation[:dst_app_name]})', RED, BOLD) %> Open Graph Translations found, please check that Open Graph stories exist and localization.")
54
+ else
55
+ translation[:src_translation_arr] = Scraper.update_display_names!(translation[:src_translation_arr], translation[:src_app_name].to_s, translation[:dst_app_name].to_s)
56
+ translations_cleanup
57
+ say("<%= color('Export Translations....', YELLOW, BOLD) %>")
58
+ choose do |menu|
59
+ menu.prompt = "This action will overwrite existing #{translation[:dst_app_name]} translations. Are you sure you want to export translations?"
60
+ menu.choice(:Yes) {
61
+ say("....starting Export!")
62
+ Browser.export
63
+ }
64
+ menu.choices(:No) { say("...exiting Exporter!") }
65
+ end
66
+ end
67
+ end
68
+
69
+ def prepare
70
+ src_translation_arr = Common.get_application_translations(translation[:source_app_token], translation[:app_locale], false)
71
+ dst_translation_arr = Common.get_application_translations(translation[:destination_app_token], translation[:app_locale], true)
72
+ translation[:src_translation_arr] = src_translation_arr
73
+ translation[:dst_translation_arr] = dst_translation_arr
74
+
75
+ if translation[:src_translation_arr].nil?
76
+ Common.show_arguments_info(translation)
77
+ say("<%= color('Invalid application data, please recheck application and locale settings.', RED, BOLD) %>")
78
+ exit
79
+ end
80
+ end
81
+
82
+ def get_apps_details(error_keys = [])
83
+ invalid_entries = false
84
+ initialize_translation
85
+
86
+ translation.each do |key, val|
87
+ if val.empty? || error_keys.include?(key.to_s)
88
+ invalid_entries = true
89
+ case key
90
+ when :source_application_id then translation[key] = ask_for_app_id(key).to_s
91
+ when :source_application_secret then translation[key] = ask_for_app_secret(key)
92
+ when :destination_application_id then translation[key] = ask_for_app_id(key).to_s
93
+ when :destination_application_secret then translation[key] = ask_for_app_secret(key)
94
+ else translation[key] = ask("\nPlease Enter <%= color('#{capitalize(key)}', GREEN, BOLD) %>", String)
95
+ end
96
+ end
97
+ end
98
+
99
+ if invalid_entries
100
+ validate_applications_data
101
+ end
102
+ end
103
+
104
+ def validate_applications_data
105
+ locales = generate_locales
106
+ error_keys = []
107
+
108
+ if translation[:source_application_id].empty?
109
+ error_keys << "source_application_id"
110
+ end
111
+ if translation[:source_application_secret].empty?
112
+ error_keys << "source_application_secret"
113
+ end
114
+ if translation[:destination_application_id].empty?
115
+ error_keys << "destination_application_token"
116
+ end
117
+ if translation[:destination_application_secret].empty?
118
+ error_keys << "destination_application_secret"
119
+ end
120
+ if translation[:source_application_id].eql?(translation[:destination_application_id])
121
+ say("<%= color('Duplicated selection: ', RED, BOLD) %> please selection different source and destination applications. \n")
122
+ error_keys << "destination_application_id" << "destination_application_secret"
123
+ end
124
+ if translation[:app_locale].empty?
125
+ error_keys << "app_locale"
126
+ else
127
+ if !locales.include?(translation[:app_locale])
128
+ error_keys << "app_locale"
129
+ end
130
+ end
131
+
132
+ if !error_keys.empty?
133
+ say("<%= color('Invalid Data: ', RED, BOLD) %> #{error_keys.join(' ')} \n")
134
+ choose do |menu|
135
+ menu.prompt = "Re-enter app details?"
136
+ menu.choice(:Yes) {
137
+ get_apps_details(error_keys)
138
+ }
139
+ menu.choices(:No) {
140
+ say("...exiting Exporter!")
141
+ exit
142
+ }
143
+ end
144
+ else
145
+ get_app_specfics
146
+ end
147
+ end
148
+
149
+ def generate_locales
150
+ locales_arr = []
151
+ app_root = File.expand_path(File.dirname(__FILE__))
152
+ fb_locales_doc = File.open(File.join(app_root, "/resources/FacebookLocales.xml"))
153
+ locales = Nokogiri::XML(fb_locales_doc)
154
+
155
+ locales.xpath("//representation").each do |locale|
156
+ locales_arr << locale.text
157
+ end
158
+
159
+ return locales_arr
160
+ end
161
+
162
+ def get_app_specfics
163
+ say("\n.....retrieving app tokens")
164
+ translation[:source_app_token] = Common.get_app_token(translation[:source_application_id], translation[:source_application_secret])
165
+ translation[:destination_app_token] = Common.get_app_token(translation[:destination_application_id], translation[:destination_application_secret])
166
+
167
+ translation[:src_app_name] = Common.get_application_name(translation[:source_app_token], translation[:source_application_id])
168
+ translation[:dst_app_name] = Common.get_application_name(translation[:destination_app_token], translation[:destination_application_id])
169
+ end
170
+
171
+ def initialize_translation
172
+ @@translation ||= {:source_application_id => '', :source_application_secret => '', :destination_application_id => '', :destination_application_secret => '', :app_locale => '' }
173
+ end
174
+
175
+ def translations_cleanup
176
+ Common.update_destination_translations(translation)
177
+ Common.remove_empty_translations(translation)
178
+ Common.show_translations_info(translation)
179
+ end
180
+
181
+ def capitalize(sym)
182
+ sym.to_s.split('_').map(&:capitalize).join(' ')
183
+ end
184
+
185
+ def ask_for_app_id(translation_key)
186
+ ask("\nPlease Enter <%= color('#{capitalize(translation_key)}', GREEN, BOLD) %>", lambda { |id| id.to_s.strip } ) do |q|
187
+ q.validate = lambda { |p| (p =~ /^\d{15}$/) != nil }
188
+ q.responses[:not_valid] = "Please enter a 15 digit App Id."
189
+ end
190
+ end
191
+
192
+ def ask_for_app_secret(translation_key)
193
+ ask("\nPlease Enter <%= color('#{capitalize(translation_key)}', GREEN, BOLD) %>", lambda { |id| id.to_s.strip } ) do |q|
194
+ q.validate = lambda { |p| (p =~ /^[a-zA-Z0-9]{32}$/) != nil }
195
+ q.responses[:not_valid] = "Please enter a 32 character alphanumeric App Secret."
196
+ end
197
+ end
198
+
199
+ def display_app_splash
200
+ puts "\n"
201
+ splash = ConsoleSplash.new(6, 88)
202
+ splash.write_horizontal_pattern("||")
203
+ splash.write_vertical_pattern("||")
204
+ splash.write_center(-4, "Open Graph Translations Exporter")
205
+ splash.write_center(-3, "Version: #{OpengraphTransporter::VERSION}")
206
+ splash.splash
207
+ puts "\n"
208
+ end
209
+
210
+ end
211
+
212
+
213
+ end
214
+ end
@@ -0,0 +1,99 @@
1
+ module OpengraphTransporter
2
+ class Browser
3
+
4
+ MAX_TRANSLATION_PAGE_LIMIT = 30
5
+
6
+ class << self
7
+
8
+ def export
9
+ begin
10
+ Watir.driver = :webdriver
11
+ @browser = Watir::Browser.new :firefox
12
+ @page_index_counter = 0
13
+ @translation_count_index = 0
14
+ @translation = Base.translation
15
+ @translation_arr = @translation [:dst_translation_arr].clone
16
+ fb_login
17
+
18
+ developer_translations_home_uri = "https://www.facebook.com/translations/admin/browse.php?search=&sloc=en_US&aloc=#{@translation[:app_locale]}&app=#{@translation[:destination_application_id]}"
19
+ @browser.goto developer_translations_home_uri
20
+ GracefulQuit.enable
21
+ parse_translation_rows
22
+
23
+ rescue Exception => e
24
+ say("An error occurred when generating automated browser, do you have FireFox installed? \n\n error: #{e}.")
25
+ exit
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def parse_translation_rows
32
+ @translation_arr.each_with_index do |translation, index|
33
+ row_hash = "variant:" << translation[:native_hash]
34
+ translation_row = @browser.tr(:id, row_hash)
35
+ if translation_row.exists? && !translation[:translation].empty?
36
+ say("updating translation native hash: #{translation[:native_hash]} : #{translation[:translation]}")
37
+ translation_row.td(:class, /s_trans/).click
38
+ translation_row.textarea(:class, /uiTextareaAutogrow/).set translation[:translation]
39
+ translation_row.button(:id, /submit:/).click
40
+ sleep(0.25)
41
+ @translation_count_index += 1
42
+ end
43
+ GracefulQuit.check
44
+ end
45
+ process_next_page
46
+ end
47
+
48
+ def process_next_page
49
+ next_button = @browser.div(:class => "pagerpro_container").link(:text => /Next/)
50
+ if next_button.exists? && @page_index_counter < MAX_TRANSLATION_PAGE_LIMIT
51
+ begin
52
+ translations_page = @page_index_counter + 1
53
+ say("processing translation page.... #{translations_page + 1}")
54
+ @page_index_counter += 1
55
+ next_button.click
56
+ sleep(2.5)
57
+ parse_translation_rows
58
+ rescue Exception => e
59
+ say("Translation error: #{e}, carry on.")
60
+ parse_translation_rows
61
+ end
62
+ else
63
+ complete_translations_process
64
+ end
65
+ end
66
+
67
+ def complete_translations_process
68
+ fb_logout
69
+ open_graph_translations_stats
70
+ end
71
+
72
+ def fb_login
73
+ say("\n.....logging into Facebook")
74
+ @browser.goto "https://www.facebook.com"
75
+ @browser.text_field(id: "email").set Base.user[:fb_username]
76
+ @browser.text_field(id: "pass").set Base.user[:fb_password]
77
+ @browser.label(id: "loginbutton").click
78
+ end
79
+
80
+ def fb_logout
81
+ say(".....logging out of Facebook \n")
82
+ @browser.div(id: "userNavigationLabel").click
83
+ @browser.label(class: "uiLinkButton navSubmenu").click
84
+ @browser.close
85
+ end
86
+
87
+ def open_graph_translations_stats
88
+ say("<%= color('\n***********************************************************************************************************', GREEN, BOLD) %>")
89
+ say("Source Application: <%= color('#{@translation[:src_app_name]}', GREEN, BOLD) %>")
90
+ say("Destination Application: <%= color('#{@translation[:dst_app_name]}', GREEN, BOLD) %>")
91
+ say("Export completed: <%= color('#{@translation[:dst_translation_arr].length} Open Graph translations processed', GREEN, BOLD) %>")
92
+ say("<%= color('***********************************************************************************************************\n', GREEN, BOLD) %>")
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+ end
99
+
@@ -0,0 +1,159 @@
1
+ module OpengraphTransporter
2
+ class Common
3
+
4
+ FB_GRAPH_HOST = "https://graph.facebook.com"
5
+
6
+ class << self
7
+
8
+ def update_destination_translations(translation)
9
+ say(".....updating translation text")
10
+ translation[:dst_translation_arr].each do |dst_translation|
11
+ translation[:src_translation_arr].each do |src_translation|
12
+ if dst_translation[:native].eql?(src_translation['native'])
13
+ dst_translation[:translation] = src_translation['translation']
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ def remove_empty_translations(translation)
20
+ say(".....remove empty translations")
21
+ translation[:dst_translation_arr].each_with_index do |dst_translation, index|
22
+ if dst_translation[:translation].empty?
23
+ translation[:dst_translation_arr].delete_at(index)
24
+ end
25
+ end
26
+ end
27
+
28
+ def get_application_translations(app_token, app_locale, include_hash)
29
+ if include_hash
30
+ fql = %Q{select native_hash, native_string, best_string FROM translation WHERE locale = "#{app_locale}"}
31
+ mappings = {"native_hash" => "native_hash", "native_string" => "native", "best_string" => "translation"}
32
+ else
33
+ fql = %Q{select native_string, best_string FROM translation WHERE locale = "#{app_locale}"}
34
+ mappings = {"native_string" => "native", "best_string" => "translation"}
35
+ end
36
+
37
+ params = {:access_token => app_token, :q => fql}
38
+ request = 'fql'
39
+ app_translations = fb_graph_call(request, params)['data']
40
+ @app_translations = Array.new
41
+
42
+ app_translations.each do |translation|
43
+ @app_translations.push(Hash[translation.map {|k, v| [mappings[k], v] }])
44
+ end
45
+
46
+ if !include_hash
47
+ @app_translations = @app_translations.uniq!
48
+ end
49
+ return @app_translations
50
+ end
51
+
52
+ def get_application_details(app_token, app_id)
53
+ fql = %Q{select display_name, description, link, namespace from application where app_id = #{app_id}}
54
+ params = {:access_token => app_token, :q => fql}
55
+ request = 'fql'
56
+ response = fb_graph_call(request, params)
57
+ return response['data'][0]
58
+ end
59
+
60
+ def show_translations_info(translation)
61
+ say("<%= color('\n***********************************************************************************************************', YELLOW, BOLD) %>")
62
+ say("Source Application ID: <%= color('#{translation[:source_application_id]}', BOLD) %> Existing translations: <%= color('#{translation[:src_translation_arr].length}', YELLOW, BOLD) %> Application Name: <%= color('#{translation[:src_app_name]}', BOLD) %>")
63
+ say("Destination Application ID: <%= color('#{translation[:destination_application_id]}', BOLD) %> Existing translations: <%= color('#{translation[:dst_translation_arr].length}', YELLOW, BOLD) %> Application Name: <%= color('#{translation[:dst_app_name]}', BOLD) %>")
64
+ say("<%= color('***********************************************************************************************************\n', YELLOW, BOLD) %>")
65
+ end
66
+
67
+
68
+ def show_arguments_info(translation)
69
+ say("<%= color('\n***********************************************************************************************************', YELLOW, BOLD) %>")
70
+ say("Source Application ID: <%= color('#{Base.translation[:source_application_id]}', YELLOW, BOLD) %> Application Name: <%= color('#{Base.translation[:src_app_name]}', YELLOW, BOLD) %>")
71
+ say("Destination Application ID: <%= color('#{Base.translation[:destination_application_id]}', YELLOW, BOLD) %> Application Name: <%= color('#{Base.translation[:dst_app_name]}', YELLOW, BOLD) %>")
72
+ say("Selected Locale: <%= color('#{Base.translation[:app_locale]}', YELLOW, BOLD) %>")
73
+ say("<%= color('***********************************************************************************************************\n', YELLOW, BOLD) %>")
74
+ end
75
+
76
+ def get_application_name(app_token, app_id)
77
+ params = {:access_token => app_token}
78
+ request = "#{app_id}"
79
+ response = fb_graph_call(request, params)
80
+ if response.nil?
81
+ show_arguments_info(translation)
82
+ say("<%= color('Invalid application data, please recheck application and locale settings.', RED, BOLD) %>")
83
+ exit
84
+ end
85
+ return response['name']
86
+ end
87
+
88
+ def get_app_token(app_id, app_secret)
89
+ params = {
90
+ :client_id => app_id,
91
+ :client_secret => app_secret,
92
+ :grant_type => 'client_credentials'
93
+ }
94
+ path = "/oauth/access_token"
95
+ response = fb_graph_call(path, params, {:format => :text})
96
+
97
+ if response.nil?
98
+ say("<%= color('Invalid Data:', RED, BOLD) %> please recheck source and destination app_id and app_secret.")
99
+ exit
100
+ end
101
+
102
+ dummy = Addressable::URI.new
103
+ dummy.query = response
104
+ access_token = dummy.query_values["access_token"]
105
+
106
+ return access_token
107
+ end
108
+
109
+
110
+ private
111
+
112
+ def fb_graph_call(path, params = {}, options = {})
113
+ clnt = HTTPClient.new
114
+ uri = Addressable::URI.parse(FB_GRAPH_HOST)
115
+ uri.path = path
116
+ query = params
117
+ # default to JSON response
118
+ options = {:format => :json}.merge(options)
119
+ begin
120
+ response = clnt.get(uri, query)
121
+ if response.status == 200
122
+ case options[:format]
123
+ when :text then response.body
124
+ else JSON.parse(response.content)
125
+ end
126
+ end
127
+ rescue StandardError => e
128
+ say("Graph Call error: #{e}")
129
+ say("...exiting Exporter!")
130
+ exit
131
+ # raise e
132
+ end
133
+ end
134
+
135
+ # unused
136
+ def write_csv(translations_arr)
137
+ unless translations_arr.length == 0
138
+ csv_file_name = "appid_#{@translation[:source_application_id]}.csv"
139
+ csv_tmp_file = Tempfile.new(csv_file_name)
140
+ headers_arr = Array.new
141
+ translations_arr[0].each_key {|key| headers_arr.push(key) }
142
+
143
+ CSV.open(csv_tmp_file, "w") do |csv|
144
+ csv << headers_arr
145
+ translations_arr.each do |el|
146
+ csv << el.values
147
+ end
148
+ end
149
+
150
+ send_file csv_tmp_file, :type => 'text/csv', :disposition => 'download', :filename => csv_file_name
151
+ csv_tmp_file.close
152
+ csv_tmp_file.unlink
153
+ end
154
+ end
155
+
156
+ end
157
+
158
+ end
159
+ end
@@ -0,0 +1,33 @@
1
+ require "singleton"
2
+ module OpengraphTransporter
3
+
4
+ class GracefulQuit
5
+ include Singleton
6
+
7
+ attr_accessor :breaker
8
+
9
+ def initialize
10
+ self.breaker = false
11
+ end
12
+
13
+ class << self
14
+
15
+ def enable
16
+ trap('INT') {
17
+ yield if block_given?
18
+ self.instance.breaker = true
19
+ }
20
+ end
21
+
22
+ def check(message = "Quitting Exporter")
23
+ if self.instance.breaker
24
+ yield if block_given?
25
+ puts message
26
+ exit
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end