rosette-core 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +26 -0
  3. data/History.txt +3 -0
  4. data/README.md +94 -0
  5. data/Rakefile +18 -0
  6. data/lib/rosette/core.rb +110 -0
  7. data/lib/rosette/core/branch_utils.rb +152 -0
  8. data/lib/rosette/core/commands.rb +139 -0
  9. data/lib/rosette/core/commands/errors.rb +17 -0
  10. data/lib/rosette/core/commands/git/commit_command.rb +65 -0
  11. data/lib/rosette/core/commands/git/diff_base_command.rb +301 -0
  12. data/lib/rosette/core/commands/git/diff_command.rb +188 -0
  13. data/lib/rosette/core/commands/git/diff_entry.rb +44 -0
  14. data/lib/rosette/core/commands/git/fetch_command.rb +27 -0
  15. data/lib/rosette/core/commands/git/repo_snapshot_command.rb +40 -0
  16. data/lib/rosette/core/commands/git/show_command.rb +70 -0
  17. data/lib/rosette/core/commands/git/snapshot_command.rb +50 -0
  18. data/lib/rosette/core/commands/git/status_command.rb +128 -0
  19. data/lib/rosette/core/commands/git/with_non_merge_ref.rb +48 -0
  20. data/lib/rosette/core/commands/git/with_ref.rb +92 -0
  21. data/lib/rosette/core/commands/git/with_refs.rb +92 -0
  22. data/lib/rosette/core/commands/git/with_repo_name.rb +50 -0
  23. data/lib/rosette/core/commands/git/with_snapshots.rb +45 -0
  24. data/lib/rosette/core/commands/queuing/enqueue_commit_command.rb +37 -0
  25. data/lib/rosette/core/commands/queuing/requeue_commit_command.rb +46 -0
  26. data/lib/rosette/core/commands/translations/export_command.rb +257 -0
  27. data/lib/rosette/core/commands/translations/translation_lookup_command.rb +66 -0
  28. data/lib/rosette/core/commands/translations/with_locale.rb +47 -0
  29. data/lib/rosette/core/configurator.rb +160 -0
  30. data/lib/rosette/core/error_reporters/buffered_error_reporter.rb +96 -0
  31. data/lib/rosette/core/error_reporters/error_reporter.rb +31 -0
  32. data/lib/rosette/core/error_reporters/nil_error_reporter.rb +25 -0
  33. data/lib/rosette/core/error_reporters/printing_error_reporter.rb +58 -0
  34. data/lib/rosette/core/error_reporters/raising_error_reporter.rb +27 -0
  35. data/lib/rosette/core/errors.rb +93 -0
  36. data/lib/rosette/core/extractor/commit_log.rb +33 -0
  37. data/lib/rosette/core/extractor/commit_log_status.rb +57 -0
  38. data/lib/rosette/core/extractor/commit_processor.rb +109 -0
  39. data/lib/rosette/core/extractor/extractor.rb +72 -0
  40. data/lib/rosette/core/extractor/extractor_config.rb +74 -0
  41. data/lib/rosette/core/extractor/locale.rb +118 -0
  42. data/lib/rosette/core/extractor/phrase.rb +76 -0
  43. data/lib/rosette/core/extractor/phrase/phrase_index_policy.rb +108 -0
  44. data/lib/rosette/core/extractor/phrase/phrase_to_hash.rb +33 -0
  45. data/lib/rosette/core/extractor/repo_config.rb +339 -0
  46. data/lib/rosette/core/extractor/serializer_config.rb +55 -0
  47. data/lib/rosette/core/extractor/static_extractor.rb +44 -0
  48. data/lib/rosette/core/extractor/translation.rb +44 -0
  49. data/lib/rosette/core/extractor/translation/translation_to_hash.rb +28 -0
  50. data/lib/rosette/core/git/diff_finder.rb +131 -0
  51. data/lib/rosette/core/git/ref.rb +116 -0
  52. data/lib/rosette/core/git/repo.rb +378 -0
  53. data/lib/rosette/core/path_matcher_factory.rb +330 -0
  54. data/lib/rosette/core/resolvers/extractor_id.rb +37 -0
  55. data/lib/rosette/core/resolvers/integration_id.rb +37 -0
  56. data/lib/rosette/core/resolvers/preprocessor_id.rb +38 -0
  57. data/lib/rosette/core/resolvers/resolver.rb +115 -0
  58. data/lib/rosette/core/resolvers/serializer_id.rb +37 -0
  59. data/lib/rosette/core/snapshots/cached_head_snapshot_factory.rb +51 -0
  60. data/lib/rosette/core/snapshots/cached_snapshot_factory.rb +67 -0
  61. data/lib/rosette/core/snapshots/head_snapshot_factory.rb +58 -0
  62. data/lib/rosette/core/snapshots/repo_config_path_filter.rb +83 -0
  63. data/lib/rosette/core/snapshots/snapshot_factory.rb +184 -0
  64. data/lib/rosette/core/string_utils.rb +23 -0
  65. data/lib/rosette/core/translation_status.rb +81 -0
  66. data/lib/rosette/core/validators.rb +18 -0
  67. data/lib/rosette/core/validators/commit_validator.rb +62 -0
  68. data/lib/rosette/core/validators/commits_validator.rb +32 -0
  69. data/lib/rosette/core/validators/encoding_validator.rb +32 -0
  70. data/lib/rosette/core/validators/locale_validator.rb +37 -0
  71. data/lib/rosette/core/validators/repo_validator.rb +33 -0
  72. data/lib/rosette/core/validators/serializer_validator.rb +37 -0
  73. data/lib/rosette/core/validators/validator.rb +31 -0
  74. data/lib/rosette/core/version.rb +8 -0
  75. data/lib/rosette/data_stores.rb +11 -0
  76. data/lib/rosette/data_stores/errors.rb +26 -0
  77. data/lib/rosette/data_stores/phrase_status.rb +59 -0
  78. data/lib/rosette/integrations.rb +12 -0
  79. data/lib/rosette/integrations/errors.rb +15 -0
  80. data/lib/rosette/integrations/integratable.rb +58 -0
  81. data/lib/rosette/integrations/integration.rb +23 -0
  82. data/lib/rosette/preprocessors.rb +11 -0
  83. data/lib/rosette/preprocessors/errors.rb +14 -0
  84. data/lib/rosette/preprocessors/preprocessor.rb +48 -0
  85. data/lib/rosette/queuing.rb +14 -0
  86. data/lib/rosette/queuing/commits.rb +19 -0
  87. data/lib/rosette/queuing/commits/commit_conductor.rb +90 -0
  88. data/lib/rosette/queuing/commits/commit_job.rb +93 -0
  89. data/lib/rosette/queuing/commits/commits_queue_configurator.rb +60 -0
  90. data/lib/rosette/queuing/commits/extract_stage.rb +46 -0
  91. data/lib/rosette/queuing/commits/fetch_stage.rb +51 -0
  92. data/lib/rosette/queuing/commits/finalize_stage.rb +76 -0
  93. data/lib/rosette/queuing/commits/phrase_storage_granularity.rb +20 -0
  94. data/lib/rosette/queuing/commits/push_stage.rb +91 -0
  95. data/lib/rosette/queuing/commits/stage.rb +96 -0
  96. data/lib/rosette/queuing/job.rb +74 -0
  97. data/lib/rosette/queuing/queue.rb +28 -0
  98. data/lib/rosette/queuing/queue_configurator.rb +76 -0
  99. data/lib/rosette/queuing/worker.rb +30 -0
  100. data/lib/rosette/serializers.rb +10 -0
  101. data/lib/rosette/serializers/serializer.rb +98 -0
  102. data/lib/rosette/tms.rb +9 -0
  103. data/lib/rosette/tms/repository.rb +95 -0
  104. data/rosette-core.gemspec +24 -0
  105. data/spec/core/branch_utils_spec.rb +110 -0
  106. data/spec/core/commands/git/commit_command_spec.rb +60 -0
  107. data/spec/core/commands/git/diff_command_spec.rb +263 -0
  108. data/spec/core/commands/git/fetch_command_spec.rb +61 -0
  109. data/spec/core/commands/git/repo_snapshot_command_spec.rb +72 -0
  110. data/spec/core/commands/git/show_command_spec.rb +128 -0
  111. data/spec/core/commands/git/snapshot_command_spec.rb +86 -0
  112. data/spec/core/commands/git/status_command_spec.rb +154 -0
  113. data/spec/core/commands/queuing/enqueue_commit_command_spec.rb +34 -0
  114. data/spec/core/commands/queuing/requeue_commit_command_spec.rb +46 -0
  115. data/spec/core/commands/translations/export_command_spec.rb +113 -0
  116. data/spec/core/commands/translations/translation_lookup_command_spec.rb +58 -0
  117. data/spec/core/configurator_spec.rb +47 -0
  118. data/spec/core/error_reporters/buffered_error_reporter_spec.rb +61 -0
  119. data/spec/core/error_reporters/nil_error_reporter_spec.rb +16 -0
  120. data/spec/core/error_reporters/printing_error_reporter_spec.rb +60 -0
  121. data/spec/core/extractor/commit_log_status_spec.rb +216 -0
  122. data/spec/core/extractor/commit_processor_spec.rb +68 -0
  123. data/spec/core/extractor/extractor_config_spec.rb +47 -0
  124. data/spec/core/extractor/extractor_spec.rb +26 -0
  125. data/spec/core/extractor/locale_spec.rb +92 -0
  126. data/spec/core/extractor/phrase/phrase_index_policy_spec.rb +116 -0
  127. data/spec/core/extractor/phrase/phrase_to_hash_spec.rb +18 -0
  128. data/spec/core/extractor/repo_config_spec.rb +147 -0
  129. data/spec/core/extractor/translation/translation_to_hash_spec.rb +25 -0
  130. data/spec/core/git/diff_finder_spec.rb +74 -0
  131. data/spec/core/git/ref_spec.rb +118 -0
  132. data/spec/core/git/repo_spec.rb +216 -0
  133. data/spec/core/path_matcher_factory_spec.rb +139 -0
  134. data/spec/core/resolvers/extractor_id_spec.rb +47 -0
  135. data/spec/core/resolvers/integration_id_spec.rb +47 -0
  136. data/spec/core/resolvers/preprocessor_id_spec.rb +47 -0
  137. data/spec/core/resolvers/serializer_id_spec.rb +47 -0
  138. data/spec/core/snapshots/snapshot_factory_spec.rb +145 -0
  139. data/spec/core/string_utils_spec.rb +19 -0
  140. data/spec/core/translation_status_spec.rb +91 -0
  141. data/spec/core/validators/commit_validator_spec.rb +40 -0
  142. data/spec/core/validators/encoding_validator_spec.rb +30 -0
  143. data/spec/core/validators/locale_validator_spec.rb +31 -0
  144. data/spec/core/validators/repo_validator_spec.rb +30 -0
  145. data/spec/core/validators/serializer_validator_spec.rb +31 -0
  146. data/spec/integrations/integratable_spec.rb +58 -0
  147. data/spec/queuing/commits/commit_conductor_spec.rb +71 -0
  148. data/spec/queuing/commits/commit_job_spec.rb +87 -0
  149. data/spec/queuing/commits/extract_stage_spec.rb +68 -0
  150. data/spec/queuing/commits/fetch_stage_spec.rb +101 -0
  151. data/spec/queuing/commits/finalize_stage_spec.rb +88 -0
  152. data/spec/queuing/commits/push_stage_spec.rb +145 -0
  153. data/spec/queuing/commits/stage_spec.rb +80 -0
  154. data/spec/queuing/job_spec.rb +33 -0
  155. data/spec/queuing/queue_configurator_spec.rb +44 -0
  156. data/spec/spec_helper.rb +90 -0
  157. data/spec/test_helpers/fake_commit_stage.rb +17 -0
  158. metadata +257 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1e9c9e17cffceee3657ed1dce1aba43c06e66873
4
+ data.tar.gz: 4b0170aa075e3ba248ccfe49dc8d042fb6578d35
5
+ SHA512:
6
+ metadata.gz: c4fc6828341aafeed4ccfbd42034337c001ff0562f234d29f2d00fc296b10af0e74ee4fded6b8cb0f3498d7f5932a1f9edcff715f86af618936242947ff04788
7
+ data.tar.gz: 415b95e177ab76034b84f3262fa93c3d5c02c3b18552e2a9040dc06653036d1958b143ccd495ce376e12454f2a9f2c4c37fe5f2d616afed66ab1b6fea215d951
data/Gemfile ADDED
@@ -0,0 +1,26 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ ruby '2.0.0', engine: 'jruby', engine_version: '1.7.15'
6
+
7
+ gem 'rosette-datastore-memory', github: 'rosette-proj/rosette-datastore-memory'
8
+
9
+ group :development, :test do
10
+ gem 'activemodel', '>= 3.2.20'
11
+ gem 'expert', '~> 1.0'
12
+ gem 'pry-nav'
13
+ gem 'rake'
14
+ gem 'repo-fixture'
15
+ end
16
+
17
+ group :development do
18
+ gem 'yard', '~> 0.8.0'
19
+ end
20
+
21
+ group :test do
22
+ gem 'codeclimate-test-reporter', require: nil
23
+ gem 'simplecov'
24
+ gem 'rspec'
25
+ gem 'rosette-test-helpers', github: 'rosette-proj/rosette-test-helpers'
26
+ end
@@ -0,0 +1,3 @@
1
+ == 1.0.0
2
+
3
+ * Birthday!
@@ -0,0 +1,94 @@
1
+ rosette-core
2
+ ========
3
+
4
+ [![Build Status](https://travis-ci.org/rosette-proj/rosette-core.svg?branch=master)](https://travis-ci.org/rosette-proj/rosette-core.svg?branch=master) [![Code Climate](https://codeclimate.com/github/rosette-proj/rosette-core/badges/gpa.svg)](https://codeclimate.com/github/rosette-proj/rosette-core) [![Test Coverage](https://codeclimate.com/github/rosette-proj/rosette-core/badges/coverage.svg)](https://codeclimate.com/github/rosette-proj/rosette-core/coverage)
5
+
6
+ ## Installation
7
+
8
+ `gem install rosette-core`
9
+
10
+ ## Usage
11
+
12
+ ```ruby
13
+ require 'rosette/core'
14
+ ```
15
+
16
+ ## Intro
17
+
18
+ This repository contains the core classes for the Rosette internationalizaton framework. Full documentation can be found on [rubydoc.info](http://www.rubydoc.info/github/rosette-proj/rosette-core).
19
+
20
+ Generally, this library is required by other projects like [rosette-server](https://github.com/rosette-proj/rosette-server), which means that most likely you'll make use of it indirectly. Please refer to the documentation that accompanies these other projects to set up and use Rosette.
21
+
22
+ ## Major Components
23
+
24
+ What follows is a list of the major components in rosette-core. It's not meant to be an exhaustive list, but should give a decent overview of what's going on.
25
+
26
+ ### Commands
27
+
28
+ Rosette commands are designed to mimic git operations. For example, use the `DiffCommand` to show the added/removed/changed phrases between two git refs. Use the `StatusCommand` to compute the status of a git ref i.e. the percent translated per locale. Commands all follow the builder pattern, meaning you create a blank instance, call setter methods for required fields, then call the `#execute` method. Here's an example for the `ShowCommand`:
29
+
30
+ ```ruby
31
+ Rosette::Core::Commands::ShowCommand.new(rosette_config)
32
+ .set_repo_name('my_awesome_repo')
33
+ .set_ref('master')
34
+ .execute
35
+ ```
36
+
37
+ ### Git Classes
38
+
39
+ Rosette manages git repos by leveraging the open-source [jGit library](https://eclipse.org/jgit), which is a full git implementation for the JVM. The `Repo` and `DiffFinder` classes wrap jGit and provide a set of methods for interacting with git repositories in a slightly more Ruby-ish way. Specifically, `Repo` offers generic methods like `#parents_of` and `#read_object_bytes`, while `DiffFinder` finds diffs between git refs. Here's an example of creating a `Repo` instance and calculating a diff:
40
+
41
+ ```ruby
42
+ repo = Rosette::Core::Repo.from_path('/path/to/my_repo/.git')
43
+ repo.diff('master', 'my_branch') # you can optionally specify a list of paths as well
44
+ ```
45
+
46
+ ### Resolvers
47
+
48
+ The resolvers provide a way of looking up class constants for a number of Rosette's sub-components using slash-separated strings. For example, the `ExtractorId` class will resolve the string 'xml/android' into `Rosette::Extractors::XmlExtractor::AndroidExtractor`. Resolvers exist for extractors, integrations, pre-processors, and serializers.
49
+
50
+ ```ruby
51
+ Rosette::Core::ExtractorId.resolve('xml/android')
52
+ Rosette::Core::SerializerId.resolve('yaml/rails')
53
+ ```
54
+
55
+ ### Snapshot Classes
56
+
57
+ Snapshots are Ruby hashes of file paths to git commit ids. The idea of the snapshot is central to Rosette's git model in that Rosette uses them to know when files that contain translatable content last changed. Rather than storing phrases for every file for every commit, Rosette only stores content when files change. Rosette can use a snapshot to gather a complete list of phrases for each commit in the repository.
58
+
59
+ You probably won't have to take snapshots manually, but if you do, here's an example:
60
+
61
+ ```ruby
62
+ Rosette::Core::SnapshotFactory.new
63
+ .set_repo_config(repo_config)
64
+ .set_start_commit_id('abc123')
65
+ .set_paths('path/to/snapshot') # looks at all paths in repo by default
66
+ .take_snapshot
67
+ ```
68
+
69
+ ### Queue Classes
70
+
71
+ You can choose to process new commits by placing them in a queue (perhaps in combination with a github webhook or a plain 'ol git hook). The queuing logic and various queue stages all live in rosette-core. The stages are:
72
+
73
+ 1. Fetch/pull the repo
74
+ 2. Extract phrases for the given commit, store in datastore
75
+ 3. Push phrases to TMS (translation management system) for translation
76
+ 4. Finalize the TMS submission (perform any necessary clean-up)
77
+
78
+ If you're not using a queue to process new commits, you'll have to process them some other way to stay current.
79
+
80
+ ### Interfaces
81
+
82
+ rosette-core contains a few base classes that serve as interfaces for implementations that live in other gems. For example, the [rosette-extractor-yaml gem](https://github.com/rosette-proj/rosette-extractor-yaml) defines the `YamlExtractor` class, which inherits from `Rosette::Core::Extractor`. Other interfaces include `Rosette::Tms::Repository`, `Rosette::Serializers::Serializer`, `Rosette::Preprocessors::Preprocessor`, and more.
83
+
84
+ ## Requirements
85
+
86
+ All Rosette components only run under jRuby. Java dependencies are managed via the [expert gem](https://github.com/camertron/expert). Run `bundle exec expert install` to install Java dependencies.
87
+
88
+ ## Running Tests
89
+
90
+ `bundle`, then `bundle exec expert install`, then `bundle exec rspec`.
91
+
92
+ ## Authors
93
+
94
+ * Cameron C. Dutro: http://github.com/camertron
@@ -0,0 +1,18 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
4
+
5
+ require 'bundler'
6
+ require 'rspec/core/rake_task'
7
+ require 'rubygems/package_task'
8
+
9
+ require './lib/rosette/core'
10
+
11
+ Bundler::GemHelper.install_tasks
12
+
13
+ task :default => :spec
14
+
15
+ desc 'Run specs'
16
+ RSpec::Core::RakeTask.new do |t|
17
+ t.pattern = './spec/**/*_spec.rb'
18
+ end
@@ -0,0 +1,110 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'logger'
4
+ require 'rosette/core/errors'
5
+
6
+ java_import java.lang.System
7
+
8
+ # Rosette is a modular internationalization platform written in Ruby.
9
+ module Rosette
10
+ # Get the current Rosette logger. Defaults to a logger that logs to STDOUT.
11
+ #
12
+ # @return [#info, #warning, #error] The current logger.
13
+ def self.logger
14
+ @logger ||= Logger.new(STDOUT)
15
+ end
16
+
17
+ # Set the Rosette logger.
18
+ #
19
+ # @param [#info, #warning, #error] new_logger The new logger.
20
+ # @return [void]
21
+ def self.logger=(new_logger)
22
+ @logger = new_logger
23
+ end
24
+
25
+ # Get the current Rosette environment name. Defaults to "development".
26
+ #
27
+ # @return [String] The current environment name.
28
+ def self.env
29
+ @env || 'development'
30
+ end
31
+
32
+ # Set the Rosette environment name.
33
+ #
34
+ # @param [String] new_env The new environment name.
35
+ # @return [void]
36
+ def self.env=(new_env)
37
+ @env = new_env
38
+ end
39
+
40
+ # Constructs a new Rosette configurator object and yields it to the block.
41
+ #
42
+ # @return [Configurator] The configurator object that was yielded.
43
+ # @yield [configuration]
44
+ # @yieldparam configuration [Configurator]
45
+ def self.build_config
46
+ configuration = Rosette::Core::Configurator.new
47
+ yield configuration
48
+ configuration.apply_integrations(configuration)
49
+ configuration
50
+ end
51
+
52
+ # Namespace for all Rosette core classes.
53
+ module Core
54
+ # The default encoding for all of Rosette.
55
+ DEFAULT_ENCODING = Encoding::UTF_8
56
+
57
+ autoload :Configurator, 'rosette/core/configurator'
58
+
59
+ autoload :StringUtils, 'rosette/core/string_utils'
60
+
61
+ autoload :Ref, 'rosette/core/git/ref'
62
+ autoload :Repo, 'rosette/core/git/repo'
63
+ autoload :DiffFinder, 'rosette/core/git/diff_finder'
64
+
65
+ autoload :Snapshot, 'rosette/core/snapshots/snapshot'
66
+ autoload :SnapshotFactory, 'rosette/core/snapshots/snapshot_factory'
67
+ autoload :CachedSnapshotFactory, 'rosette/core/snapshots/cached_snapshot_factory'
68
+ autoload :HeadSnapshotFactory, 'rosette/core/snapshots/head_snapshot_factory'
69
+ autoload :CachedHeadSnapshotFactory, 'rosette/core/snapshots/cached_head_snapshot_factory'
70
+ autoload :SnapshotFilterable, 'rosette/core/snapshots/snapshot_filterable'
71
+ autoload :RepoConfigPathFilter, 'rosette/core/snapshots/repo_config_path_filter'
72
+
73
+ autoload :Extractor, 'rosette/core/extractor/extractor'
74
+ autoload :StaticExtractor, 'rosette/core/extractor/static_extractor'
75
+ autoload :Phrase, 'rosette/core/extractor/phrase'
76
+ autoload :Translation, 'rosette/core/extractor/translation'
77
+ autoload :CommitLog, 'rosette/core/extractor/commit_log'
78
+ autoload :CommitLogStatus, 'rosette/core/extractor/commit_log_status'
79
+ autoload :ExtractorConfig, 'rosette/core/extractor/extractor_config'
80
+ autoload :PathMatcherFactory, 'rosette/core/path_matcher_factory'
81
+ autoload :TranslationsPathConfig, 'rosette/core/extractor/translations_path_config'
82
+ autoload :RepoConfig, 'rosette/core/extractor/repo_config'
83
+ autoload :SerializerConfig, 'rosette/core/extractor/serializer_config'
84
+ autoload :CommitProcessor, 'rosette/core/extractor/commit_processor'
85
+ autoload :Locale, 'rosette/core/extractor/locale'
86
+
87
+ autoload :PhraseIndexPolicy, 'rosette/core/extractor/phrase/phrase_index_policy'
88
+ autoload :PhraseToHash, 'rosette/core/extractor/phrase/phrase_to_hash'
89
+
90
+ autoload :TranslationToHash, 'rosette/core/extractor/translation/translation_to_hash'
91
+
92
+ autoload :ErrorReporter, 'rosette/core/error_reporters/error_reporter'
93
+ autoload :NilErrorReporter, 'rosette/core/error_reporters/nil_error_reporter'
94
+ autoload :PrintingErrorReporter, 'rosette/core/error_reporters/printing_error_reporter'
95
+ autoload :BufferedErrorReporter, 'rosette/core/error_reporters/buffered_error_reporter'
96
+ autoload :RaisingErrorReporter, 'rosette/core/error_reporters/raising_error_reporter'
97
+
98
+ autoload :Validators, 'rosette/core/validators'
99
+
100
+ autoload :Resolver, 'rosette/core/resolvers/resolver'
101
+ autoload :ExtractorId, 'rosette/core/resolvers/extractor_id'
102
+ autoload :IntegrationId, 'rosette/core/resolvers/integration_id'
103
+ autoload :SerializerId, 'rosette/core/resolvers/serializer_id'
104
+ autoload :PreprocessorId, 'rosette/core/resolvers/preprocessor_id'
105
+
106
+ autoload :Commands, 'rosette/core/commands'
107
+ autoload :BranchUtils, 'rosette/core/branch_utils'
108
+ autoload :TranslationStatus, 'rosette/core/translation_status'
109
+ end
110
+ end
@@ -0,0 +1,152 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'digest/sha1'
4
+
5
+ module Rosette
6
+ module Core
7
+
8
+ # Utility methods that work at the branch level.
9
+ class BranchUtils
10
+ class << self
11
+
12
+ # Computes the status for an array of commit logs. Determining the
13
+ # aggregate status of more than one commit log means choosing the least
14
+ # far-along status from the list. In other words, if the list contains
15
+ # two commit logs with FETCHED and FINALIZED statuses respectively, this
16
+ # method will return FETCHED because that's the least advanced status in
17
+ # the list. A list of commit logs is only as advanced as its 'weakest',
18
+ # least advanced element.
19
+ #
20
+ # @param [Array<CommitLog>] commit_logs The list of commit logs to
21
+ # derive the status from.
22
+ # @return [PhraseStatus]
23
+ def derive_status_from(commit_logs)
24
+ ps = Rosette::DataStores::PhraseStatus
25
+ entry = commit_logs.min_by { |entry| ps.index(entry.status) }
26
+
27
+ if entry
28
+ entry.status
29
+ else
30
+ # since FINALIZED commit_logs aren't considered, the absence of any
31
+ # commit logs (i.e. a nil entry) indicates the branch is fully
32
+ # processed
33
+ Rosette::DataStores::PhraseStatus::FINALIZED
34
+ end
35
+ end
36
+
37
+ # Computes the phrase count for the given list of commit logs. This is
38
+ # simply the sum of all the commit log's phrase counts.
39
+ #
40
+ # @param [Array<CommitLog>] commit_logs The list of commit logs to
41
+ # compute the phrase count from.
42
+ # @return [Fixnum]
43
+ def derive_phrase_count_from(commit_logs)
44
+ commit_logs.inject(0) { |sum, entry| sum + entry.phrase_count }
45
+ end
46
+
47
+ # Computes the status for each individual locale contained in the
48
+ # given list of commit logs. Locale statuses are hashes that contain a
49
+ # count of the number of translated phrases for that locale as well as
50
+ # the percent translated.
51
+ #
52
+ # @param [Array<CommitLog>] commit_logs The list of commit logs to
53
+ # compute locale statuses from.
54
+ # @param [String] repo_name The name of the repo these commit logs came
55
+ # from.
56
+ # @param [Object] datastore The datastore to query for locale data.
57
+ # @param [Fixnum] phrase_count The aggregate number of phrases for the
58
+ # list of commit logs. If +nil+, the +derive_phrase_count+ method
59
+ # will be called to fill in this parameter.
60
+ # @return [Hash] the locale status hash. Contains locale codes as
61
+ # top-level keys and hashes as values. Each locale hash contains the
62
+ # keys +:translated_count+ and +:percent_translated+.
63
+ def derive_locale_statuses_from(commit_logs, repo_name, datastore, phrase_count = nil)
64
+ phrase_count ||= derive_phrase_count_from(commit_logs)
65
+
66
+ locale_statuses = commit_logs.each_with_object({}) do |commit_log, ret|
67
+ locale_entries = datastore.commit_log_locales_for(
68
+ repo_name, commit_log.commit_id
69
+ )
70
+
71
+ locale_entries.each do |locale_entry|
72
+ ret[locale_entry.locale] ||= { translated_count: 0 }
73
+
74
+ ret[locale_entry.locale].tap do |locale_hash|
75
+ locale_hash[:translated_count] += locale_entry.translated_count
76
+ end
77
+ end
78
+ end
79
+
80
+ add_translation_percentages(locale_statuses, phrase_count)
81
+ end
82
+
83
+ # Adds any missing locale keys to a hash of locale statuses.
84
+ #
85
+ # @param [Array<Locale>] all_locales The complete list of locales that
86
+ # +locale_statuses+ should contain.
87
+ # @param [Hash] locale_statuses The locale statuses hash such as is
88
+ # returned by +derive_locale_statuses_from+.
89
+ # @return [Hash] a copy of +locale_statuses+ that contains entries for
90
+ # all the locales in +all_locales+. Any locale entries missing from
91
+ # +locale_statuses+ will have been filled in with blank data, i.e.
92
+ # +:translated_count+ will be 0 and +:percent_translated+ will be 0.0.
93
+ def fill_in_missing_locales(all_locales, locale_statuses)
94
+ all_locales.each_with_object({}) do |locale, ret|
95
+ if found_locale_status = locale_statuses[locale.code]
96
+ ret[locale.code] = found_locale_status
97
+ else
98
+ ret[locale.code] = {
99
+ percent_translated: 0.0,
100
+ translated_count: 0
101
+ }
102
+ end
103
+ end
104
+ end
105
+
106
+ def derive_branch_name(commit_id, repo)
107
+ refs = repo.refs_containing(commit_id).map(&:getName)
108
+
109
+ if refs.include?('refs/remotes/origin/master')
110
+ 'refs/remotes/origin/master'
111
+ else
112
+ filter_refs(refs).first
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ def filter_refs(refs)
119
+ refs.each_with_object([]) do |ref, ret|
120
+ ret << ref if valid_ref?(ref)
121
+ end
122
+ end
123
+
124
+ def valid_ref?(ref_text)
125
+ ref = Rosette::Core::Ref.parse(ref_text)
126
+ ref && ref.remote? && ref.name != 'master' && ref.name != 'HEAD'
127
+ end
128
+
129
+ def add_translation_percentages(locale_statuses, phrase_count)
130
+ locale_statuses.each_with_object({}) do |(locale, locale_status), ret|
131
+ ret[locale] = locale_status.merge(
132
+ percent_translated: percentage(
133
+ locale_status.fetch(:translated_count, 0) || 0,
134
+ phrase_count || 0
135
+ )
136
+ )
137
+ end
138
+ end
139
+
140
+ def percentage(dividend, divisor)
141
+ if divisor > 0
142
+ (dividend.to_f / divisor.to_f).round(2)
143
+ else
144
+ 0.0
145
+ end
146
+ end
147
+
148
+ end
149
+ end
150
+
151
+ end
152
+ end
@@ -0,0 +1,139 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'digest/sha1'
4
+
5
+ module Rosette
6
+ module Core
7
+
8
+ # Namespace for all Rosette commands.
9
+ module Commands
10
+
11
+ autoload :Errors, 'rosette/core/commands/errors'
12
+
13
+ autoload :CommitCommand, 'rosette/core/commands/git/commit_command'
14
+ autoload :DiffBaseCommand, 'rosette/core/commands/git/diff_base_command'
15
+ autoload :DiffCommand, 'rosette/core/commands/git/diff_command'
16
+ autoload :ShowCommand, 'rosette/core/commands/git/show_command'
17
+ autoload :StatusCommand, 'rosette/core/commands/git/status_command'
18
+ autoload :FetchCommand, 'rosette/core/commands/git/fetch_command'
19
+ autoload :SnapshotCommand, 'rosette/core/commands/git/snapshot_command'
20
+ autoload :RepoSnapshotCommand, 'rosette/core/commands/git/repo_snapshot_command'
21
+ autoload :WithRepoName, 'rosette/core/commands/git/with_repo_name'
22
+ autoload :WithRef, 'rosette/core/commands/git/with_ref'
23
+ autoload :WithRefs, 'rosette/core/commands/git/with_refs'
24
+ autoload :WithNonMergeRef, 'rosette/core/commands/git/with_non_merge_ref'
25
+ autoload :WithSnapshots, 'rosette/core/commands/git/with_snapshots'
26
+ autoload :DiffEntry, 'rosette/core/commands/git/diff_entry'
27
+
28
+ autoload :WithLocale, 'rosette/core/commands/translations/with_locale'
29
+ autoload :ExportCommand, 'rosette/core/commands/translations/export_command'
30
+ autoload :TranslationLookupCommand, 'rosette/core/commands/translations/translation_lookup_command'
31
+
32
+ autoload :EnqueueCommitCommand, 'rosette/core/commands/queuing/enqueue_commit_command'
33
+ autoload :RequeueCommitCommand, 'rosette/core/commands/queuing/requeue_commit_command'
34
+
35
+ # Base class for all Rosette commands.
36
+ #
37
+ # @!attribute [r] configuration
38
+ # @return [Configurator] Rosette configuration.
39
+ class Command
40
+ attr_reader :configuration
41
+
42
+ class << self
43
+ # Validates a single field.
44
+ #
45
+ # @param [Symbol] field The field to validate.
46
+ # @param [Hash] validator_hash The hash of options for this
47
+ # validation. For now, should just contain +:type+ which contains
48
+ # to a symbol corresponding to the type of validator to use.
49
+ # @return [void]
50
+ def validate(field, validator_hash)
51
+ validators[field] << instantiate_validator(validator_hash)
52
+ end
53
+
54
+ # A hash of all the currently configured validators.
55
+ #
56
+ # @return [Hash<Symbol, Array<Validator>>] The hash of fields to
57
+ # valdiator instances.
58
+ def validators
59
+ @validators ||= Hash.new { |hash, key| hash[key] = [] }
60
+ end
61
+
62
+ private
63
+
64
+ def instantiate_validator(validator_hash)
65
+ validator_type = validator_hash.fetch(:type)
66
+ validator_class_for(validator_type).new(validator_hash)
67
+ end
68
+
69
+ def validator_class_for(validator_type)
70
+ klass = "#{Rosette::Core::StringUtils.camelize(validator_type.to_s)}Validator"
71
+ if Rosette::Core::Validators.const_defined?(klass)
72
+ Rosette::Core::Validators.const_get(klass)
73
+ else
74
+ raise TypeError, "couldn't find #{validator_type} validator"
75
+ end
76
+ end
77
+ end
78
+
79
+ # Creates a new command instance.
80
+ #
81
+ # @param [Configurator] configuration The Rosette configuration to use.
82
+ def initialize(configuration)
83
+ @configuration = configuration
84
+ end
85
+
86
+ # Gets the hash of current error messages.
87
+ #
88
+ # @return [Hash<Symbol, Array<String>>] The hash of current messages.
89
+ # The hash's keys are field names and the values are arrays of error
90
+ # messages for that field.
91
+ def messages
92
+ @messages ||= {}
93
+ end
94
+
95
+ # Returns true if the command's validators all pass, false otherwise.
96
+ #
97
+ # @raise [NotImplementedError]
98
+ def valid?
99
+ raise NotImplementedError, 'please use a Command subclass.'
100
+ end
101
+
102
+ protected
103
+
104
+ def datastore
105
+ configuration.datastore
106
+ end
107
+
108
+ def path_digest(paths)
109
+ Digest::MD5.hexdigest(paths.join)
110
+ end
111
+ end
112
+
113
+ # Base class for all of Rosette's git-based commands.
114
+ class GitCommand < Command
115
+ # Returns true if the command's validators all pass, false otherwise.
116
+ # After this method is finished executing, the +messages+ hash will
117
+ # have been populated with error messages per field.
118
+ #
119
+ # @return [Boolean]
120
+ def valid?
121
+ self.class.validators.all? do |name, validators|
122
+ validators.all? do |validator|
123
+ valid = validator.valid?(send(name), repo_name, configuration)
124
+ messages[name] = validator.messages unless valid
125
+ valid
126
+ end
127
+ end
128
+ end
129
+
130
+ protected
131
+
132
+ def get_repo(name)
133
+ configuration.get_repo(name)
134
+ end
135
+ end
136
+
137
+ end
138
+ end
139
+ end