terrestrial-cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +134 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +7 -0
  12. data/bin/terrestrial +44 -0
  13. data/circle.yml +11 -0
  14. data/lib/terrestrial/cli/android_xml_formatter.rb +43 -0
  15. data/lib/terrestrial/cli/android_xml_parser.rb +49 -0
  16. data/lib/terrestrial/cli/bootstrapper.rb +184 -0
  17. data/lib/terrestrial/cli/command.rb +20 -0
  18. data/lib/terrestrial/cli/detects_project_type.rb +16 -0
  19. data/lib/terrestrial/cli/dot_strings_formatter.rb +53 -0
  20. data/lib/terrestrial/cli/dot_strings_parser.rb +139 -0
  21. data/lib/terrestrial/cli/editor/android_xml.rb +64 -0
  22. data/lib/terrestrial/cli/editor/base_editor.rb +36 -0
  23. data/lib/terrestrial/cli/editor/objc.rb +66 -0
  24. data/lib/terrestrial/cli/editor/printer.rb +47 -0
  25. data/lib/terrestrial/cli/editor/storyboard.rb +98 -0
  26. data/lib/terrestrial/cli/editor/swift.rb +92 -0
  27. data/lib/terrestrial/cli/editor.rb +42 -0
  28. data/lib/terrestrial/cli/engine_mapper.rb +30 -0
  29. data/lib/terrestrial/cli/entry_collection_differ.rb +22 -0
  30. data/lib/terrestrial/cli/file_finder.rb +65 -0
  31. data/lib/terrestrial/cli/file_picker.rb +58 -0
  32. data/lib/terrestrial/cli/flight/ios_workflow.rb +81 -0
  33. data/lib/terrestrial/cli/flight/table_workflow.rb +77 -0
  34. data/lib/terrestrial/cli/flight.rb +93 -0
  35. data/lib/terrestrial/cli/ignite.rb +73 -0
  36. data/lib/terrestrial/cli/init.rb +133 -0
  37. data/lib/terrestrial/cli/mixpanel_client.rb +56 -0
  38. data/lib/terrestrial/cli/parser/android_xml.rb +82 -0
  39. data/lib/terrestrial/cli/parser/base_parser.rb +42 -0
  40. data/lib/terrestrial/cli/parser/objc.rb +127 -0
  41. data/lib/terrestrial/cli/parser/storyboard.rb +166 -0
  42. data/lib/terrestrial/cli/parser/string_analyser.rb +115 -0
  43. data/lib/terrestrial/cli/parser/swift.rb +102 -0
  44. data/lib/terrestrial/cli/parser.rb +25 -0
  45. data/lib/terrestrial/cli/photoshoot.rb +65 -0
  46. data/lib/terrestrial/cli/pull.rb +110 -0
  47. data/lib/terrestrial/cli/push.rb +40 -0
  48. data/lib/terrestrial/cli/scan.rb +72 -0
  49. data/lib/terrestrial/cli/string_registry.rb +30 -0
  50. data/lib/terrestrial/cli/terminal_ui.rb +25 -0
  51. data/lib/terrestrial/cli/variable_normalizer.rb +34 -0
  52. data/lib/terrestrial/cli/version.rb +5 -0
  53. data/lib/terrestrial/cli.rb +82 -0
  54. data/lib/terrestrial/config.rb +99 -0
  55. data/lib/terrestrial/creates_terrestrial_yml.rb +9 -0
  56. data/lib/terrestrial/web/response.rb +17 -0
  57. data/lib/terrestrial/web.rb +78 -0
  58. data/lib/terrestrial/yaml_helper.rb +48 -0
  59. data/lib/terrestrial.rb +7 -0
  60. data/terrestrial-cli.gemspec +29 -0
  61. metadata +188 -0
@@ -0,0 +1,65 @@
1
+ module Terrestrial
2
+ module Cli
3
+ class FileFinder
4
+
5
+ EXCLUDED_FOLDERS = [
6
+ /Carthage\//,
7
+ /Pods\//,
8
+ /Tests\//
9
+ ]
10
+
11
+ EXCLUDED_FILES = [
12
+ "LaunchScreen.storyboard"
13
+ ]
14
+
15
+ def self.find(directory, extensions)
16
+ self.new(directory, extensions).find
17
+ end
18
+
19
+ def initialize(directory, extensions)
20
+ @directory = directory
21
+ @extensions = extensions
22
+ end
23
+
24
+ def find
25
+ Dir[@directory + "/**/*.*"]
26
+ .map {|f| relative_path(f) }
27
+ .reject {|f| excluded_folders(f) }
28
+ .select {|f| @extensions.include?(File.extname(f)) }
29
+ .reject {|f| excluded_files(f) }
30
+ .select {|f| valid_paths(f) }
31
+ end
32
+
33
+ private
34
+
35
+ def relative_path(file)
36
+ Pathname.new(file)
37
+ .relative_path_from(Pathname.new(@directory))
38
+ .to_s
39
+ end
40
+
41
+ def valid_paths(f)
42
+ # Some files need to be in specific places to count
43
+ # as "real files". For example, strings.xml files should
44
+ # only be read if they are in /res/values/strings.xml
45
+ #
46
+ # Add rules here as needed.
47
+
48
+ case File.extname(f)
49
+ when ".xml"
50
+ f.end_with? "/res/values/strings.xml"
51
+ else
52
+ true
53
+ end
54
+ end
55
+
56
+ def excluded_folders(path)
57
+ EXCLUDED_FOLDERS.any? { |folder| path.scan(folder).any? }
58
+ end
59
+
60
+ def excluded_files(path)
61
+ EXCLUDED_FILES.any? { |name| File.basename(path) == name }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,58 @@
1
+ require 'pathname'
2
+
3
+ module Terrestrial
4
+ module Cli
5
+ class FilePicker
6
+ class << self
7
+
8
+ def run(files, platform)
9
+ if files.count == 1
10
+ single_file_workflow(files)
11
+ else
12
+ multiple_files_workflow(files)
13
+ end
14
+ end
15
+
16
+ def multiple_files_workflow(files)
17
+ puts "-- Terrestrial detected #{files.count} localization files:"
18
+ files.each_with_index do |path, i|
19
+ puts "(#{i + 1}) #{path}"
20
+ end
21
+ puts ""
22
+ puts "Select the files you want Terrestrial to track as the base localization: e.g. \"1,4,5\""
23
+ puts "(To not select any files, just hit return. You can edit tracked files in terrestrial.yml)"
24
+
25
+ result = STDIN.gets.chomp
26
+
27
+ process_result(result, files)
28
+ end
29
+
30
+ def single_file_workflow(files)
31
+ puts "Terrestrial detected #{files.count} file:"
32
+ puts "(1) #{files[0].to_s}"
33
+ puts ""
34
+ puts "Use this file as your base language file? (you can change this late in terrestrial.yml) y/n?"
35
+
36
+ result = STDIN.gets.chomp.strip
37
+
38
+ if result == "y"
39
+ files
40
+ else
41
+ []
42
+ end
43
+ end
44
+
45
+ def process_result(result, files)
46
+ if result == ""
47
+ return []
48
+ else
49
+ result
50
+ .split(",")
51
+ .map {|i| i.to_i}
52
+ .map {|i| files[i - 1]}
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,81 @@
1
+ module Terrestrial
2
+ module Cli
3
+ class Flight < Command
4
+ class IosWorkflow
5
+
6
+ def initialize(bootstrap_results)
7
+ @results = bootstrap_results
8
+ end
9
+
10
+ def run
11
+ print_instructions
12
+ command = STDIN.gets.chomp
13
+
14
+ if command == "y"
15
+ lproj_folder = TerminalUI.show_spinner do
16
+ Editor.prepare_files(results.all_occurences)
17
+ initalize_localizable_strings_files
18
+ end
19
+ add_file_to_config(lproj_folder)
20
+ print_done_message(lproj_folder)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def initalize_localizable_strings_files
27
+ path = create_path_to_localization_files
28
+
29
+ File.open(path, "a+") do |f|
30
+ formatter = DotStringsFormatter.new(results)
31
+
32
+ f.write "// Created by Terrestrial (#{Time.now.to_s})"
33
+ f.write "\n\n"
34
+ f.write formatter.format
35
+ end
36
+ path
37
+ end
38
+
39
+ def create_path_to_localization_files
40
+ folder_name = Pathname.new(Dir[Config[:directory] + "/*.xcodeproj"].first).basename(".*").to_s
41
+ base_lproj_path = FileUtils.mkdir_p(Config[:directory] + "/#{folder_name}" + "/Base.lproj").first
42
+
43
+ base_lproj_path + "/Localizable.strings"
44
+ end
45
+
46
+ def add_file_to_config(path)
47
+ path_to_file = Pathname.new(path)
48
+ current_dir = Pathname.new(Config[:directory])
49
+
50
+ Config.load({ translation_files: [
51
+ path_to_file.relative_path_from(current_dir).to_s
52
+ ]})
53
+ Config.update_project_config
54
+ end
55
+
56
+ def print_done_message(lproj_folder)
57
+ puts "------------------------------------"
58
+ puts "-- Done!"
59
+ puts "- Created Base.lproj in #{lproj_folder}."
60
+ puts "- Remember to include the new localization files in your project!"
61
+ end
62
+
63
+ def print_instructions
64
+ puts "- Terrestrial will add #{results.length} strings to your base Localizable.strings."
65
+ puts ""
66
+ puts "------------------------------------"
67
+ puts "-- Source Code"
68
+ puts "- Would you like Terrestrial to also modify the selected strings in your"
69
+ puts "- source code to call .translated?"
70
+ puts "- e.g. \"This is my string\" => \"This is my string\".translated"
71
+ puts ""
72
+ puts "y/n?"
73
+ end
74
+
75
+ def results
76
+ @results
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,77 @@
1
+ module Terrestrial
2
+ module Cli
3
+ class Flight < Command
4
+ class TableWorkflow
5
+
6
+ LOCAL_CONFIG = {
7
+ strings_per_page: 10
8
+ }
9
+
10
+ def initialize(bootstrap_results)
11
+ @results = bootstrap_results
12
+ end
13
+
14
+ def run
15
+ print_instructions
16
+
17
+ exclusions = []
18
+ i = 0
19
+
20
+ results.all_occurences.each_slice(LOCAL_CONFIG[:strings_per_page]).with_index do |five_strings, index|
21
+ puts "Page #{index + 1} of #{(results.all_occurences.count / LOCAL_CONFIG[:strings_per_page].to_f).ceil}"
22
+
23
+ table = create_string_table(five_strings, i)
24
+ i += LOCAL_CONFIG[:strings_per_page]
25
+ puts table
26
+ print_instructions
27
+ puts ""
28
+
29
+ command = STDIN.gets.chomp
30
+ if command == 'q'
31
+ abort "Aborting..."
32
+ else
33
+ begin
34
+ exclusions.concat(command.split(",").map(&:to_i))
35
+ rescue
36
+ abort "Couldn't process that command :( Aborting..."
37
+ end
38
+ end
39
+ end
40
+ exclusions
41
+ end
42
+
43
+ private
44
+
45
+ def create_string_table(strings, i)
46
+ Terminal::Table.new(headings: ['Index', 'String', 'File']) do |t|
47
+ strings.each_with_index do |string, tmp_index|
48
+ t.add_row([i, string.string, file_name_with_line_number(string)])
49
+ t.add_separator unless tmp_index == (strings.length - 1) || i == (strings.length - 1)
50
+ i += 1
51
+ end
52
+ end
53
+ end
54
+
55
+ def print_instructions
56
+ puts "-- Instructions --"
57
+ puts "- To exclude any strings from translation, type the index of each string."
58
+ puts "- e.g. 1,2,4"
59
+ puts "------------------"
60
+ puts "Any Exclusions? (press return to continue or 'q' to quit at any time)"
61
+ end
62
+
63
+ def file_name_with_line_number(string)
64
+ if string.line_number
65
+ "#{string.file}:#{string.line_number}"
66
+ else
67
+ string.file
68
+ end
69
+ end
70
+
71
+ def results
72
+ @results
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,93 @@
1
+ require 'terrestrial/cli/flight/ios_workflow.rb'
2
+ require 'terrestrial/cli/flight/table_workflow.rb'
3
+ require 'terminal-table'
4
+ require 'pathname'
5
+
6
+ module Terrestrial
7
+ module Cli
8
+ class Flight < Command
9
+
10
+ def run
11
+ Config.load!
12
+ MixpanelClient.track("cli-flight-command")
13
+
14
+ if !Config.project_config_exist?
15
+ abort_not_initialized
16
+ end
17
+ if Config[:translation_files].any?
18
+ abort_already_run_flight
19
+ end
20
+ if Config[:platform] != "ios"
21
+ puts "'flight' is not supported on Android."
22
+ puts " iOS projects often just include strings in their source code instead of extracting them into resource files."
23
+ puts " We created 'flight' to get iOS project up and running quicker."
24
+ puts " 'R.string' makes localization much easier :)"
25
+ abort
26
+ end
27
+
28
+ puts "- Finding untranslated human readable strings..."
29
+ TerminalUI.show_spinner do
30
+ find_new_strings
31
+ end
32
+
33
+ puts "------------------------------------"
34
+ puts "- Found #{strings.count} strings"
35
+ puts ""
36
+ exclusions = TableWorkflow.new(strings).run
37
+ puts "------------------------------------"
38
+ puts "- Done!"
39
+
40
+ strings.exclude_occurences(exclusions)
41
+
42
+ if Config[:platform] == "ios"
43
+ IosWorkflow.new(strings).run
44
+ elsif Config[:platform] == 'android'
45
+ android_workflow
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def android_workflow
52
+ puts "- Terrestrial will annotate the selected strings in your strings.xml file:"
53
+ puts "- e.g. <string name='my_name'>My string!</string> => <string terrestrial='true' name='my_name'>My string</string>"
54
+ end
55
+
56
+ def find_new_strings
57
+ @strings = Bootstrapper.find_new_strings(Config[:directory])
58
+ end
59
+
60
+ def strings
61
+ @strings
62
+ end
63
+
64
+ def abort_not_initialized
65
+ puts "You should initialize your project before running flight."
66
+ puts "It's simple! You can do this by running:"
67
+ puts ""
68
+ puts " terrestrial init --api-key <API KEY> --project-id <PROJECT ID>"
69
+ puts ""
70
+ puts "You can find your Api Key and Project ID at https://mission.terrestrial.io"
71
+ abort
72
+ end
73
+
74
+ def abort_already_run_flight
75
+ if Config[:platform] == "ios"
76
+ puts "Looks like you already have Localizable.strings files."
77
+ puts "'flight' scans your source code for human readable strings that have not been translated"
78
+ puts "and helps you quickstart your internaionalization process."
79
+ puts ""
80
+ puts "If you want to new strings into your .strings file, run 'terrestrial gen'. It will:"
81
+ puts " 1. Scan your source code for .translated and NSLocalizedString calls."
82
+ puts " 2. Determine if the strings already exist in Localizable.strings."
83
+ puts " 3. Append any new strings to your base Localizable.strings."
84
+ puts ""
85
+ puts "For more information, visit http://docs.terrestrial.io/, or jump on our Slack via https://terrestrial-slack.herokuapp.com/"
86
+ abort
87
+ else
88
+ # TODO
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,73 @@
1
+ module Terrestrial
2
+ module Cli
3
+ class Ignite < Command
4
+
5
+ WORKING_DIR = '/usr/local/var/terrestrial'
6
+
7
+ def run
8
+ Config.load!
9
+ MixpanelClient.track("cli-ignite-command")
10
+ lang = opts["language"]
11
+
12
+ if Config[:platform] != "ios"
13
+ abort "Unfortunately launching your app in a locale via 'ignite' is only supported on iOS at this time."
14
+ end
15
+
16
+ if lang.nil? || lang.empty?
17
+ abort "Please provide a locale to launch the simulator in.\n e.g. 'terrestrial ignite es'"
18
+ end
19
+
20
+ puts "Starting simulator in locale \"#{lang}\"..."
21
+
22
+ TerminalUI.show_spinner do
23
+ ensure_var_folder_exists
24
+
25
+ workspace = Dir["#{Config[:directory]}/*.xcworkspace"][0]
26
+ project = Dir["#{Config[:directory]}/*.xcodeproj"][0]
27
+
28
+ # Kill simulator and
29
+ system("killall \"Simulator\" &> /dev/null")
30
+ `rm -rf #{WORKING_DIR}`
31
+
32
+ if workspace
33
+ # If a workspace exists we want to build it instead of the project.
34
+ # We assume the scheme we want to use is simply the application name
35
+ app_name = File.basename(workspace).split(".").first
36
+ `xcodebuild -workspace "#{workspace}" -arch "i386" ONLY_ACTIVE_ARCH=NO VALID_ARCHS="i386 x86_64" -scheme #{app_name} -sdk iphonesimulator clean`
37
+ `xcodebuild -workspace "#{workspace}" -arch "i386" ONLY_ACTIVE_ARCH=NO VALID_ARCHS="i386 x86_64" -scheme #{app_name} -sdk iphonesimulator CONFIGURATION_BUILD_DIR=#{WORKING_DIR}`
38
+ else
39
+ app_name = File.basename(project).split(".").first
40
+ `xcodebuild -project "#{project}" -arch i386 -sdk iphonesimulator clean`
41
+ `xcodebuild -project "#{project}" -arch i386 -sdk iphonesimulator CONFIGURATION_BUILD_DIR=#{WORKING_DIR}`
42
+ end
43
+ `open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app`
44
+
45
+ # Here we literally sleep until the Simulator has been booted
46
+ wait_until_booted = %{
47
+ count=`xcrun simctl list | grep Booted | wc -l | sed -e 's/ //g'`
48
+ while [ $count -lt 1 ]
49
+ do
50
+ sleep 1
51
+ count=`xcrun simctl list | grep Booted | wc -l | sed -e 's/ //g'`
52
+ done
53
+ }
54
+ `#{wait_until_booted}`
55
+
56
+ # Here we magically find the bundle identifier of the app
57
+ command = "defaults read \"#{Dir[WORKING_DIR + '/*.app/Info.plist'].first}\" CFBundleIdentifier"
58
+ bundle_name = `#{command}`.chop
59
+
60
+ # Reinstall the app,
61
+ # Run it with the locale we want
62
+ `xcrun simctl uninstall booted #{bundle_name}`
63
+ `xcrun simctl install booted "#{Dir[WORKING_DIR + "/" + app_name + ".app"].first}"`
64
+ `xcrun simctl launch booted #{bundle_name} --args -AppleLanguages \\(#{lang}\\)`
65
+ end
66
+ end
67
+
68
+ def ensure_var_folder_exists
69
+ `mkdir -p #{WORKING_DIR}`
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,133 @@
1
+ module Terrestrial
2
+ module Cli
3
+ class Init < Command
4
+
5
+ def run
6
+ # Fail early if project already exists
7
+ Config.load!({}, project: false)
8
+ MixpanelClient.track("cli-init-command")
9
+
10
+ if Config.project_config_exist?
11
+ abort "Looks like there already exists a project in this directory. Are you in the correct folder?"
12
+ end
13
+
14
+ check_arguments
15
+ detect_platform
16
+
17
+ puts "-- Terrestrial Initializing"
18
+ puts "Adding new app! Searching for localization files..."
19
+
20
+ TerminalUI.show_spinner do
21
+ # Otherwise the whole process is too quick for the eye
22
+ sleep 2 unless Config.testing?
23
+ end
24
+ puts ""
25
+
26
+ select_translation_files
27
+ create_app_in_web
28
+
29
+ if @response.success?
30
+ update_config
31
+
32
+ puts "-- Success!"
33
+ puts "App platform added to project! You can view your app at https://mission.terrestrial.io/projects/#{Config[:project_id]}/apps/#{Config[:app_id]}"
34
+ puts ""
35
+ puts "-- What to do next?"
36
+
37
+ if @translation_files.any?
38
+ puts "Run 'terrestrial scan' to see which strings Terrestrial is currently tracking."
39
+ puts "When you're ready to upload your strings for translation, run 'terrestrial push'!"
40
+ elsif @translation_files.none? && @platform == "ios"
41
+ puts "To get started localizing your app, run 'terrestrial flight'."
42
+ puts "Terrestrial will scan your code for strings, and generate the necessary localization files."
43
+ elsif @translation_files.none? && @platform == "android"
44
+ puts "Looks like Terrestrial does not know which strings.xml files to track."
45
+ puts "To continue, add your base language strings.xml file to terrestrial.yml."
46
+ puts "When you're ready, run 'terrestrial scan' to see which strings Terrestrial is tracking, and 'terrestrial push' to upload."
47
+ end
48
+ puts ""
49
+ puts "For more information, see http://docs.terrestrial.io or jump on Slack at https://terrestrial-slack.herokuapp.com/ if you have any questions."
50
+ else
51
+ puts "Oh snap. There was an error initializing your project."
52
+ puts response.body.inspect
53
+ abort
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def select_translation_files
60
+ @tranlation_files = []
61
+
62
+ files = find_platform_translation_files
63
+ if files.any?
64
+ @tranlation_files = FilePicker.run(files, @platform)
65
+
66
+ if @tranlation_files.count == 1
67
+ puts "Tracking #{@tranlation_files.count} file!"
68
+ else
69
+ puts "Tracking #{@tranlation_files.count} files!"
70
+ end
71
+ end
72
+ end
73
+
74
+ def find_platform_translation_files
75
+ if @platform == "ios"
76
+ Dir[Config[:directory] + "/**/*.strings"].map {|f| relative_path(f) }
77
+ elsif @platform == "android"
78
+ Dir[Config[:directory] + "/**/*/res/values/strings.xml"].map {|f| relative_path(f) }
79
+ else
80
+ raise "Unknown platform #{@platform}"
81
+ end
82
+ end
83
+
84
+ def update_config
85
+ Terrestrial::Config.load({
86
+ app_id: @response.body["data"]["id"],
87
+ project_id: @project_id,
88
+ platform: @platform,
89
+ api_key: @api_key,
90
+ translation_files: @tranlation_files
91
+ })
92
+
93
+ Terrestrial::Config.update_global_config
94
+ Terrestrial::Config.update_project_config
95
+ end
96
+
97
+ def create_app_in_web
98
+ @client = Terrestrial::Web.new(@api_key)
99
+ @response = @client.create_app(@project_id, @platform)
100
+ end
101
+
102
+ def check_arguments
103
+ @api_key = opts[:api_key] || Config[:api_key] ||
104
+ abort("No api key provided. You can find your API key at https://mission.terrestrial.io/.")
105
+
106
+ @project_id = opts.fetch(:project_id) { abort(
107
+ "No project ID provided. Terrestrial needs to know which project this app belongs to.\n" +
108
+ "Visit https://mission.terrestrial.io to find your project ID."
109
+ )}
110
+ end
111
+
112
+ def detect_platform
113
+ @platform = DetectsProjectType.run
114
+ end
115
+
116
+ def project_id
117
+ @project_id
118
+ end
119
+
120
+ def api_key
121
+ @api_key
122
+ end
123
+
124
+ def relative_path(file)
125
+ current_dir = Pathname.new(Config[:directory])
126
+
127
+ Pathname.new(file)
128
+ .relative_path_from(current_dir)
129
+ .to_s
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,56 @@
1
+ require 'base64'
2
+ require 'json'
3
+
4
+ module Terrestrial
5
+ module Cli
6
+ module MixpanelClient
7
+ class << self
8
+
9
+ TOKEN = "47d6a27568a3c842ead24b14907eb04e"
10
+ URL = "https://api.mixpanel.com/track"
11
+
12
+ def track(event)
13
+ # If we're live
14
+ if Config[:api_url] == "https://mission.terrestrial.io"
15
+ `curl -silent -X POST #{URL}?data=#{format_event(event)} &`
16
+ end
17
+ end
18
+
19
+ def user_identifier
20
+ if Config[:user_id]
21
+ Config[:user_id]
22
+ else
23
+ fetch_and_save_user_id
24
+ end
25
+ end
26
+
27
+ def format_event(event)
28
+ Base64.strict_encode64(event_json(event))
29
+ end
30
+
31
+ def event_json(event)
32
+ {
33
+ event: event,
34
+ properties: {
35
+ distinct_id: user_identifier,
36
+ token: TOKEN,
37
+ time: Time.now.to_i
38
+ }
39
+ }.to_json
40
+ end
41
+
42
+ def fetch_and_save_user_id
43
+ response = Web.new.get_profile
44
+ if response.success?
45
+ id = response.body["data"]["user"]["id"]
46
+ Config.load({:user_id => id})
47
+ Config.update_global_config
48
+ id
49
+ else
50
+ "unknown"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end