nabokov 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +1 -0
  3. data/.gitignore +43 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +113 -0
  6. data/.travis.yml +14 -0
  7. data/CHANGELOG.md +5 -0
  8. data/Gemfile +9 -0
  9. data/LICENSE +21 -0
  10. data/README.md +16 -0
  11. data/Rakefile +22 -0
  12. data/bin/nabokov +5 -0
  13. data/lib/nabokov/commands/runner.rb +22 -0
  14. data/lib/nabokov/commands/setup.rb +75 -0
  15. data/lib/nabokov/commands/syncers/localizations_repo_syncer.rb +101 -0
  16. data/lib/nabokov/commands/syncers/project_syncer.rb +106 -0
  17. data/lib/nabokov/commands/syncers/syncer.rb +68 -0
  18. data/lib/nabokov/core/file_manager.rb +36 -0
  19. data/lib/nabokov/core/nabokovfile.rb +66 -0
  20. data/lib/nabokov/core/nabokovfile_content_validator.rb +51 -0
  21. data/lib/nabokov/core/nabokovfile_keys.rb +34 -0
  22. data/lib/nabokov/git/git_repo.rb +137 -0
  23. data/lib/nabokov/helpers/informator.rb +83 -0
  24. data/lib/nabokov/helpers/merger.rb +86 -0
  25. data/lib/nabokov/models/strings_file.rb +7 -0
  26. data/lib/nabokov/version.rb +8 -0
  27. data/lib/nabokov.rb +14 -0
  28. data/nabokov.gemspec +31 -0
  29. data/spec/fixtures/.DS_Store +0 -0
  30. data/spec/fixtures/README.md +1 -0
  31. data/spec/fixtures/de.strings +1 -0
  32. data/spec/fixtures/en.strings +1 -0
  33. data/spec/fixtures/nabokovfile_example.yaml +9 -0
  34. data/spec/fixtures/nabokovfile_example_invalid.yaml +2 -0
  35. data/spec/fixtures/nabokovfile_example_without_master_branch.yaml +8 -0
  36. data/spec/fixtures/test_git_setup/existed_pre_commit_file +0 -0
  37. data/spec/fixtures/test_git_setup/existed_pre_commit_file_alias +0 -0
  38. data/spec/fixtures/test_git_setup/not_executable_pre_commit_file +0 -0
  39. data/spec/fixtures/test_localizations_repo_syncer/localizations_repo_fixtures/README.md +1 -0
  40. data/spec/fixtures/test_localizations_repo_syncer/nabokovfile.yaml +8 -0
  41. data/spec/fixtures/test_project_syncer/localizations_repo_fixtures/de.strings +2 -0
  42. data/spec/fixtures/test_project_syncer/localizations_repo_fixtures/en.strings +2 -0
  43. data/spec/fixtures/test_project_syncer/project_repo_fixtures/de.strings +2 -0
  44. data/spec/fixtures/test_project_syncer/project_repo_fixtures/en.strings +2 -0
  45. data/spec/fixtures/test_project_syncer/project_repo_fixtures/nabokovfile.yaml +9 -0
  46. data/spec/lib/nabokov/commands/localizations_repo_syncer_spec.rb +137 -0
  47. data/spec/lib/nabokov/commands/project_syncer_spec.rb +61 -0
  48. data/spec/lib/nabokov/commands/runner_spec.rb +7 -0
  49. data/spec/lib/nabokov/commands/setup_spec.rb +101 -0
  50. data/spec/lib/nabokov/commands/syncer_spec.rb +23 -0
  51. data/spec/lib/nabokov/core/file_manager_spec.rb +115 -0
  52. data/spec/lib/nabokov/core/nabokovfile_content_validator_spec.rb +155 -0
  53. data/spec/lib/nabokov/core/nabokovfile_keyes_spec.rb +31 -0
  54. data/spec/lib/nabokov/core/nabokovfile_spec.rb +53 -0
  55. data/spec/lib/nabokov/git/git_repo_spec.rb +670 -0
  56. data/spec/lib/nabokov/helpers/informator_spec.rb +49 -0
  57. data/spec/lib/nabokov/helpers/merger_spec.rb +114 -0
  58. data/spec/lib/nabokov/models/strings_file_spec.rb +7 -0
  59. data/spec/spec_helper.rb +11 -0
  60. metadata +238 -0
@@ -0,0 +1,66 @@
1
+ require "yaml"
2
+ require "uri"
3
+ require "nabokov/core/nabokovfile_keys"
4
+ require "nabokov/core/nabokovfile_content_validator"
5
+
6
+ module Nabokov
7
+ # Class represents the nabokovfile with the settings for nabokov
8
+ class Nabokovfile
9
+ # @return [String] The localizations repo url string
10
+ attr_accessor :localizations_repo_url
11
+ # @return [String] The localizations repo master branch
12
+ attr_accessor :localizations_repo_master_branch
13
+ # @return [String] The localizations repo local path
14
+ attr_accessor :localizations_repo_local_path
15
+ # @return [Hash] The Hash with key as localization name and value as repspected localization file path
16
+ attr_accessor :project_localization_file_paths
17
+ # @return [String] The project repo local path
18
+ attr_accessor :project_local_path
19
+
20
+ def initialize(path)
21
+ raise "Path is a required parameter" if path.nil?
22
+ raise "Couldn't find nabokov file at '#{path}'" unless File.exist?(path)
23
+ nabokovfile = File.read(path)
24
+ yaml_data = read_data_from_yaml_file(nabokovfile, path)
25
+ validate_content(yaml_data)
26
+ read_content(yaml_data)
27
+ end
28
+
29
+ def localizations_repo_local_path
30
+ return @localizations_repo_local_path unless @localizations_repo_local_path.nil?
31
+ @localizations_repo_local_path ||= ""
32
+ end
33
+
34
+ private
35
+
36
+ def read_data_from_yaml_file(yaml_file, path)
37
+ YAML.load(yaml_file)
38
+ rescue Psych::SyntaxError
39
+ raise "File at '#{path}' doesn't have a legit YAML syntax"
40
+ end
41
+
42
+ def validate_content(content_hash)
43
+ validator = NabokovfileContentValidator.new(content_hash)
44
+ validator.validate
45
+ end
46
+
47
+ def read_content(content_hash)
48
+ localizations_repo = content_hash[NabokovfileKeyes.localizations_repo]
49
+ self.localizations_repo_url = localizations_repo[NabokovfileKeyes.localizations_repo_url]
50
+ self.localizations_repo_master_branch = localizations_repo[NabokovfileKeyes.localizations_repo_master_branch].nil? ? "master" : localizations_repo[NabokovfileKeyes.localizations_repo_master_branch]
51
+ project_repo = content_hash[NabokovfileKeyes.project_repo]
52
+ self.project_localization_file_paths = project_repo[NabokovfileKeyes.project_localization_file_paths]
53
+ self.project_local_path = project_repo[NabokovfileKeyes.project_local_path]
54
+ self.localizations_repo_local_path = build_localization_local_path
55
+ end
56
+
57
+ def build_localization_local_path
58
+ repo_url_path = URI(self.localizations_repo_url).path.to_s
59
+ home_dir = Dir.home.to_s
60
+ repo_url_name_without_extension = File.basename(repo_url_path, File.extname(repo_url_path)).downcase
61
+ repo_url_organization = File.dirname(repo_url_path).downcase
62
+ nabokov_dir_name = "/.nabokov"
63
+ home_dir + nabokov_dir_name + repo_url_organization + "/" + repo_url_name_without_extension
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,51 @@
1
+ require "uri"
2
+ require "nabokov/core/nabokovfile_keys"
3
+
4
+ module Nabokov
5
+ # This class is responsible for nabokovfile content validation
6
+ # The validation rules are for localizations_repo and project_repo settings
7
+ class NabokovfileContentValidator
8
+ attr_accessor :nabokovfile_hash
9
+
10
+ def initialize(nabokovfile_hash)
11
+ self.nabokovfile_hash = nabokovfile_hash
12
+ end
13
+
14
+ # Performs validation
15
+ # First rule: localizations_repo should be the type of Hash
16
+ # localizations_repo_url should be valid URL with secure https scheme
17
+ # Second rule: project_repo should be the type of Hash
18
+ # project localizations_key should be the of Hash
19
+ # project_localization_file_paths should point to existed files
20
+ # project_local_path should point to valid folder
21
+ def validate
22
+ validate_localizations_repo
23
+ validate_project_repo
24
+ end
25
+
26
+ private
27
+
28
+ def validate_localizations_repo
29
+ localizations_repo = self.nabokovfile_hash[NabokovfileKeyes.localizations_repo]
30
+ raise "Localizations repo must be a type of Hash" unless localizations_repo.kind_of?(Hash)
31
+
32
+ url = localizations_repo[NabokovfileKeyes.localizations_repo_url]
33
+ raise "'#{url}' is not a valid URL" unless url =~ URI.regexp
34
+ raise "Please use 'https://...' instead of '#{url}' only supports encrypted requests" unless url.start_with?("https://")
35
+ end
36
+
37
+ def validate_project_repo
38
+ project_repo = self.nabokovfile_hash[NabokovfileKeyes.project_repo]
39
+ raise "Project repo must be a type of Hash" unless project_repo.kind_of?(Hash)
40
+
41
+ localizations_key = NabokovfileKeyes.project_localization_file_paths
42
+ localizations = project_repo[localizations_key]
43
+ raise "Localizations must be a type of Hash" unless localizations.kind_of?(Hash)
44
+ localizations.each_value { |path| raise "Couldn't find strings file at '#{path}'" unless File.exist?(path) }
45
+
46
+ project_local_path_key = NabokovfileKeyes.project_local_path
47
+ project_local_path = project_repo[project_local_path_key]
48
+ raise "Project repo local path must be presented" if project_local_path.nil?
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,34 @@
1
+ module Nabokov
2
+ # Class contains the named constants for nabokovfile keyes
3
+ class NabokovfileKeyes
4
+ # @return [String] The key to get the localizations repo settings
5
+ def self.localizations_repo
6
+ return "localizations_repo"
7
+ end
8
+
9
+ # @return [String] The key to get the localizations remote URL string
10
+ def self.localizations_repo_url
11
+ return "url"
12
+ end
13
+
14
+ # @return [String] The key to get the master branch of the localizations repo
15
+ def self.localizations_repo_master_branch
16
+ return "master_branch"
17
+ end
18
+
19
+ # @return [String] The key to get the project repo settings
20
+ def self.project_repo
21
+ return "project_repo"
22
+ end
23
+
24
+ # @return [String] The key to get the (localization_code => localization_file_path) hash
25
+ def self.project_localization_file_paths
26
+ return "localizations"
27
+ end
28
+
29
+ # @return [String] The key to get the project's local path
30
+ def self.project_local_path
31
+ return "local_path"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,137 @@
1
+ require "git"
2
+ require "uri"
3
+ require "securerandom"
4
+ require "pathname"
5
+
6
+ module Nabokov
7
+ # This class is basically the wrapper aroung the ruby-git gem
8
+ # Note: the ruby gem is taken not from gem spec repo, but from https://github.com/Antondomashnev/ruby-git
9
+ class GitRepo
10
+ # @return [String] Repo's remote URL, could be nil
11
+ attr_accessor :remote_url
12
+ # @return [String] Local path to the repo
13
+ attr_accessor :local_path
14
+ # @return [Git::Base] Underlying git repo
15
+ attr_reader :git_repo
16
+
17
+ def initialize(local_path, remote_url = nil, git_repo = nil)
18
+ raise "local_path is a required parameter" if local_path.nil?
19
+ expanded_local_path = File.expand_path(local_path)
20
+ @local_pathname = Pathname.new(expanded_local_path)
21
+ @git_repo = git_repo
22
+ self.remote_url = remote_url
23
+ self.local_path = expanded_local_path
24
+ end
25
+
26
+ def clone
27
+ raise "Git repo has been already cloned at '#{self.local_path}', please use 'init' instead" if repo_exist_at_local_path
28
+ @git_repo ||= Git.clone(self.remote_url, @local_pathname.basename.to_s, path: @local_pathname.parent.to_s)
29
+ end
30
+
31
+ def init
32
+ raise "Git repo has not been cloned yet from '#{self.remote_url}', please use 'clone' instead" unless repo_exist_at_local_path
33
+ @git_repo ||= Git.init(self.local_path)
34
+ end
35
+
36
+ def add(file_path)
37
+ raise "Could not find any file to add at path '#{file_path}'" unless File.exist?(file_path)
38
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before adding new files to the index" if @git_repo.nil?
39
+ @git_repo.add(file_path)
40
+ end
41
+
42
+ def commit(message = nil)
43
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before commiting new files" if @git_repo.nil?
44
+ message ||= "Automatic commit by nabokov"
45
+ @git_repo.commit(message)
46
+ end
47
+
48
+ def push
49
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before pushing any changes to remote" if @git_repo.nil?
50
+ @git_repo.push
51
+ end
52
+
53
+ def pull
54
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before pushing any changes to remote" if @git_repo.nil?
55
+ @git_repo.pull
56
+ end
57
+
58
+ def checkout_branch(name)
59
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before checkouting any branch" if @git_repo.nil?
60
+ raise "branch name could not be nil or zero length" if name.nil? || name.length.zero?
61
+ if @git_repo.is_branch?(name)
62
+ @git_repo.checkout(name)
63
+ else
64
+ @git_repo.checkout(name, { new_branch: true })
65
+ end
66
+ end
67
+
68
+ def delete_branch(name)
69
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before deleting any branch" if @git_repo.nil?
70
+ raise "branch name could not be nil or zero length" if name.nil? || name.length.zero?
71
+ @git_repo.branch(name).delete
72
+ end
73
+
74
+ def merge_branches(original_branch, branch_to_be_merged)
75
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before merging any branches" if @git_repo.nil?
76
+ raise "original branch name could not be nil or zero length" if original_branch.nil? || original_branch.length.zero?
77
+ raise "branch to be merged in name could not be nil or zero length" if branch_to_be_merged.nil? || branch_to_be_merged.length.zero?
78
+ @git_repo.branch(original_branch).merge(@git_repo.branch(branch_to_be_merged))
79
+ end
80
+
81
+ def changes?
82
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before checking if the git repo has changes" if @git_repo.nil?
83
+ return true if @git_repo.status.deleted.count > 0
84
+ return true if @git_repo.status.added.count > 0
85
+ return true if @git_repo.status.changed.count > 0
86
+ false
87
+ end
88
+
89
+ def unfinished_merge?
90
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before checking if the git repo has unfinished merge" if @git_repo.nil?
91
+ return @git_repo.has_unmerged_files?
92
+ end
93
+
94
+ def current_branch
95
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before getting the current branch" if @git_repo.nil?
96
+ return @git_repo.current_branch
97
+ end
98
+
99
+ def abort_merge
100
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before aborting merge" if @git_repo.nil?
101
+ raise "nothing to abort - git repo doesn't have unfinished merge" unless self.unfinished_merge?
102
+ @git_repo.abort_merge
103
+ end
104
+
105
+ def unmerged_files
106
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before asking for unmerged files" if @git_repo.nil?
107
+ return [] unless @git_repo.has_unmerged_files?
108
+ conflicted_files = []
109
+ @git_repo.each_conflict do |file, your_version, their_version|
110
+ conflicted_files << file
111
+ end
112
+ conflicted_files
113
+ end
114
+
115
+ def reset_to_commit(commit_sha, options = {})
116
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before resetting" if @git_repo.nil?
117
+ raise "'commit' is a required parameter and could not be nil" if commit_sha.nil?
118
+ if options[:hard]
119
+ @git_repo.reset_hard(commit_sha)
120
+ else
121
+ @git_repo.reset(commit_sha)
122
+ end
123
+ end
124
+
125
+ def log(number_of_commits)
126
+ raise "'git' is not initialized yet, please call either 'clone' or 'init' before getting the log" if @git_repo.nil?
127
+ commits = @git_repo.log(number_of_commits)
128
+ commits.map(&:sha)
129
+ end
130
+
131
+ private
132
+
133
+ def repo_exist_at_local_path
134
+ Dir.exist?(self.local_path)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,83 @@
1
+ require "cork"
2
+
3
+ module Nabokov
4
+ # This class is heavily based on the Interviewer class from the Danger gem
5
+ # The original link is https://github.com/danger/danger/blob/master/lib/danger/commands/init_helpers/interviewer.rb
6
+ class Informator
7
+ attr_accessor :no_waiting, :ui
8
+
9
+ def initialize(cork_board)
10
+ @ui = cork_board
11
+ end
12
+
13
+ def show_prompt
14
+ ui.print("> ".bold.green)
15
+ end
16
+
17
+ def say(message)
18
+ ui.puts(message)
19
+ end
20
+
21
+ def inform(message)
22
+ ui.puts(message.green)
23
+ end
24
+
25
+ def important(message)
26
+ i = message.length + 8
27
+ inform("-" * i)
28
+ inform("--- " + message + " ---")
29
+ inform("-" * i)
30
+ end
31
+
32
+ def warn(message)
33
+ ui.puts(message.yellow)
34
+ end
35
+
36
+ def error(message)
37
+ ui.puts(message.red)
38
+ end
39
+
40
+ def wait_for_return
41
+ STDOUT.flush
42
+ STDIN.gets unless @no_waiting
43
+ ui.puts
44
+ end
45
+
46
+ def ask_with_answers(question, possible_answers)
47
+ ui.print("\n#{question}? [")
48
+
49
+ print_info = proc do
50
+ possible_answers.each_with_index do |answer, i|
51
+ the_answer = i.zero? ? answer.underline : answer
52
+ ui.print " " + the_answer
53
+ ui.print(" /") if i != possible_answers.length - 1
54
+ end
55
+ ui.print " ]\n"
56
+ end
57
+ print_info.call
58
+
59
+ answer = ""
60
+
61
+ loop do
62
+ show_prompt
63
+ answer = @no_waiting ? possible_answers[0].downcase : STDIN.gets.downcase.chomp
64
+
65
+ answer = "yes" if answer == "y"
66
+ answer = "no" if answer == "n"
67
+
68
+ # default to first answer
69
+ if answer == ""
70
+ answer = possible_answers[0].downcase
71
+ ui.puts "Using: " + answer.yellow
72
+ end
73
+
74
+ break if possible_answers.map(&:downcase).include? answer
75
+
76
+ ui.print "\nPossible answers are ["
77
+ print_info.call
78
+ end
79
+
80
+ answer
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,86 @@
1
+ require "nabokov/git/git_repo"
2
+ require "nabokov/helpers/informator"
3
+
4
+ module Nabokov
5
+ # Result of the merge action perfomed by Nabokov::Merger
6
+ class MergerResult
7
+ SUCCEEDED = "succeeded".freeze
8
+ ABORTED = "aborted".freeze
9
+ end
10
+
11
+ # Class is responsible for merging two branches
12
+ class Merger
13
+ def initialize(informator, git_repo, rescue_commit_sha = nil)
14
+ raise "'informator' is a required parameter" if informator.nil?
15
+ raise "'git_repo' is a required parameter" if git_repo.nil?
16
+ @rescue_commit_sha = rescue_commit_sha
17
+ @git_repo = git_repo
18
+ @informator = informator
19
+ end
20
+
21
+ # Merges one branch with another branch
22
+ # It handles the situation when there are merge conflicts and provides
23
+ # the interface to the user to resolve the conflicts
24
+ def merge(head, branch)
25
+ @git_repo.merge_branches(head, branch)
26
+ MergerResult::SUCCEEDED
27
+ rescue Git::GitExecuteError
28
+ rescue_merge
29
+ end
30
+
31
+ private
32
+
33
+ def rescue_merge
34
+ ui.error("Merge failed with conflicts. Nabokov needs your help to continue")
35
+ proceed_option = ui.ask_with_answers("Would you like to resolve the conflicts manually or abort the synchronization?\n", ["resolve", "abort"])
36
+ if proceed_option == "abort"
37
+ abort_merge
38
+ elsif proceed_option == "resolve"
39
+ resolve_merge
40
+ end
41
+ end
42
+
43
+ def abort_merge
44
+ @git_repo.abort_merge
45
+ @git_repo.reset_to_commit(@rescue_commit_sha, { hard: true }) unless @rescue_commit_sha.nil?
46
+ MergerResult::ABORTED
47
+ end
48
+
49
+ def resolve_merge
50
+ ui.say("Great! Please resolve conflict in the following files:")
51
+ unmerged_files = @git_repo.unmerged_files
52
+ unmerged_files.each do |file|
53
+ file_path = @git_repo.local_path + "/" + file
54
+ ui.say("* #{file_path}")
55
+ end
56
+ ui.say("Please press return when you're ready to move on...")
57
+ ui.wait_for_return
58
+ commit_after_merge_resolving(unmerged_files)
59
+ MergerResult::SUCCEEDED
60
+ end
61
+
62
+ def commit_after_merge_resolving(merged_files)
63
+ commit_merge = proc do
64
+ merged_files.each do |file|
65
+ file_path = @git_repo.local_path + "/" + file
66
+ ui.say("Adding #{file_path} to git index...")
67
+ @git_repo.add(file_path)
68
+ end
69
+ ui.say("Commiting merge conflicts resolving...")
70
+ @git_repo.commit("Nabokov merge conflicts manually have been resolved...")
71
+ end
72
+
73
+ if @git_repo.changes?
74
+ commit_merge.call
75
+ else
76
+ ui.warn("Seems like you haven't resolved the merge, if you want to continue anyway please press return...")
77
+ ui.wait_for_return
78
+ commit_merge.call if @git_repo.changes?
79
+ end
80
+ end
81
+
82
+ def ui
83
+ @informator
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,7 @@
1
+ module Nabokov
2
+ class StringsFile
3
+ def self.extension
4
+ return "strings"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Nabokov
2
+ VERSION = "0.1.0".freeze
3
+ DESCRIPTION = "One of the way to work on the localization - store it in the separate github repo
4
+ and asks localization team to update files in that repo.
5
+ This solution brings as well advantages but as well a disadvantages, to minimize the disadvantages
6
+ nabokov was invented. It helps developer to automatically keep remote localzation up to date and
7
+ also remain the project localization up to date as well.".freeze
8
+ end
data/lib/nabokov.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "nabokov/version"
2
+ require "nabokov/core/nabokovfile"
3
+ require "nabokov/commands/runner"
4
+ require "nabokov/commands/syncers/syncer"
5
+ require "nabokov/commands/syncers/localizations_repo_syncer"
6
+ require "nabokov/commands/syncers/project_syncer"
7
+ require "nabokov/commands/setup"
8
+
9
+ require "claide"
10
+ require "pathname"
11
+ require "cork"
12
+
13
+ module Nabokov
14
+ end
data/nabokov.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "nabokov/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "nabokov"
8
+ spec.version = Nabokov::VERSION
9
+ spec.authors = ["Anton Domashnev"]
10
+ spec.email = ["antondomashnev@gmail.com"]
11
+ spec.description = Nabokov::DESCRIPTION
12
+ spec.summary = "Move mobile localization boringness away from you 🏃"
13
+ spec.homepage = "https://github.com/Antondomashnev/nabokov"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = ">= 2.0.0"
22
+
23
+ spec.add_runtime_dependency "rspec", "~> 3.4"
24
+ spec.add_runtime_dependency "git", "~> 1.0"
25
+ spec.add_runtime_dependency "claide", "~> 1.0"
26
+ spec.add_runtime_dependency "cork", "~> 0.1"
27
+
28
+ spec.add_development_dependency "rubocop", "~> 0.42"
29
+ spec.add_development_dependency "bundler", "~> 1.3"
30
+ spec.add_development_dependency "rake"
31
+ end
Binary file
@@ -0,0 +1 @@
1
+ Nabokov template repo for specs
@@ -0,0 +1 @@
1
+ "hello" = "hallo";
@@ -0,0 +1 @@
1
+ "hello" = "hello";
@@ -0,0 +1,9 @@
1
+ localizations_repo:
2
+ url: 'https://github.com/Antondomashnev/nabokov_example.git'
3
+ master_branch: 'shmaster'
4
+
5
+ project_repo:
6
+ localizations:
7
+ :en: 'spec/fixtures/en.strings'
8
+ :de: 'spec/fixtures/de.strings'
9
+ local_path: 'spec/fixtures/local_project'
@@ -0,0 +1,2 @@
1
+ localizations_repo: 'bla_bla_bla'
2
+ localizations fake
@@ -0,0 +1,8 @@
1
+ localizations_repo:
2
+ url: 'https://github.com/Antondomashnev/nabokov_example.git'
3
+
4
+ project_repo:
5
+ localizations:
6
+ :en: 'spec/fixtures/en.strings'
7
+ :de: 'spec/fixtures/de.strings'
8
+ local_path: 'spec/fixtures/local_project'
@@ -0,0 +1 @@
1
+ Nabokov template repo for specs
@@ -0,0 +1,8 @@
1
+ localizations_repo:
2
+ url: 'https://github.com/Antondomashnev/nabokov_example.git'
3
+
4
+ project_repo:
5
+ localizations:
6
+ :en: 'spec/fixtures/en.strings'
7
+ :de: 'spec/fixtures/de.strings'
8
+ local_path: 'spec/fixtures/local_project'
@@ -0,0 +1,2 @@
1
+ "hello" = "Hallo";
2
+ "bye" = "Tschuss";
@@ -0,0 +1,2 @@
1
+ "hello" = "Hello";
2
+ "bye" = "Bye";
@@ -0,0 +1,2 @@
1
+ "hello" = "Hello";
2
+ "bye" = "Bye";
@@ -0,0 +1,2 @@
1
+ "hello" = "Hello";
2
+ "bye" = "Bye";
@@ -0,0 +1,9 @@
1
+ localizations_repo:
2
+ url: 'https://github.com/Antondomashnev/nabokov_example.git'
3
+ master_branch: 'master'
4
+
5
+ project_repo:
6
+ localizations:
7
+ :en: 'spec/fixtures/test_project_syncer/project_repo/en.strings'
8
+ :de: 'spec/fixtures/test_project_syncer/project_repo/de.strings'
9
+ local_path: 'spec/fixtures/test_project_syncer/project_repo'