rosette-core 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,330 @@
1
+ # encoding: UTF-8
2
+
3
+ module Rosette
4
+ module Core
5
+ # Constructs condition trees for path matchers.
6
+ #
7
+ # @see ExtractorConfig
8
+ class PathMatcherFactory
9
+ # Creates a new empty node that can be used as the root of a
10
+ # conditions tree.
11
+ #
12
+ # @return [Node] the new empty node
13
+ def self.create_root
14
+ Node.new
15
+ end
16
+
17
+ # Facilitates creating operator nodes that can perform binary
18
+ # operations, like "and", "or", and "not".
19
+ module NodeOperatorFactory
20
+ # Creates an {OrNode} for combining two nodes together with a
21
+ # logical "or".
22
+ #
23
+ # @param [Node] right The other node. The left node is +self+.
24
+ # @return [OrNode] a node representing the logical "or" of +self+
25
+ # and +right+.
26
+ def or(right)
27
+ OrNode.new(self, right)
28
+ end
29
+
30
+ # Creates an {AndNode} for combining two nodes together with a
31
+ # logical "and".
32
+ #
33
+ # @param [Node] right The other node. The left node is +self+.
34
+ # @return [AndNode] a node representing the logical "and" of +self+
35
+ # and +right+.
36
+ def and(right)
37
+ AndNode.new(self, right)
38
+ end
39
+
40
+ # Creates a {NotNode} for negating +self+.
41
+ #
42
+ # @return [NotNode] a node representing the negation of +self+.
43
+ def not
44
+ NotNode.new(self)
45
+ end
46
+ end
47
+
48
+ # Provides common methods for creating nodes.
49
+ module NodeFactory
50
+ # Creates a {FileExtensionNode}.
51
+ #
52
+ # @param [String] extension The file extension to match.
53
+ # @return [FileExtensionNode]
54
+ def match_file_extension(extension)
55
+ FileExtensionNode.new(extension)
56
+ end
57
+
58
+ # Creates a bunch of {FileExtensionNode}s combined using a logical "or".
59
+ #
60
+ # @param [Array<String>] extensions A list of file extensions.
61
+ # @return [FileExtensionNode, OrNode] the root node of a tree of all
62
+ # the file extensions specified in +extensions+. Each file extension
63
+ # will be wrapped in a {FileExtensionNode} and logically "or"ed
64
+ # together. If +extensions+ only contains one file extension, then this
65
+ # method just returns an instance of {FileExtensionNode}. If +extensions+
66
+ # contains more than one entry, this method returns an {OrNode}.
67
+ def match_file_extensions(extensions)
68
+ Array(extensions).inject(nil) do |node, extension|
69
+ new_node = match_file_extension(extension)
70
+ node ? node.or(new_node) : new_node
71
+ end
72
+ end
73
+
74
+ # Creates a {PathNode}.
75
+ #
76
+ # @param [String] path The path to match.
77
+ # @return [PathNode]
78
+ def match_path(path)
79
+ PathNode.new(path)
80
+ end
81
+
82
+ # Creates a bunch of {PathNode}s combined using a logical "or".
83
+ #
84
+ # @param [Array<String>] paths A list of paths.
85
+ # @return [PathNode, OrNode] the root of a tree of all the paths specified
86
+ # in +paths+. Each path will be wrapped in a {PathNode} and logically
87
+ # "or"ed together. If +paths+ only contains one path, then this method
88
+ # just returns an instance of {PathNode}. If +paths+ contains more than
89
+ # one entry, this method returns an {OrNode}.
90
+ def match_paths(paths)
91
+ Array(paths).inject(nil) do |node, path|
92
+ new_node = match_path(path)
93
+ node ? node.or(new_node) : new_node
94
+ end
95
+ end
96
+
97
+ # Creates a {RegexNode}.
98
+ #
99
+ # @param [Regexp] regex The regex to match.
100
+ # @return [RegexNode]
101
+ def match_regex(regex)
102
+ RegexNode.new(regex)
103
+ end
104
+
105
+ # Creates a bunch of {RegexNode}s combined using a logical "or".
106
+ #
107
+ # @param [Array<Regexp>] regexes A list of regular expressions.
108
+ # @return [RegexNode, OrNode] the root of a tree of all the regexes specified
109
+ # in +regexes+. Each regex will be wrapped in a {RegexNode} and logically
110
+ # "or"ed together. If +regexes+ only contains one entry, then this method
111
+ # just returns an instance of {RegexNode}. If +regexes+ contains more than
112
+ # one entry, this method returns an {OrNode}.
113
+ def match_regexes(regexes)
114
+ Array(regexes).inject(nil) do |node, regex|
115
+ new_node = match_regex(regex)
116
+ node ? node.or(new_node) : new_node
117
+ end
118
+ end
119
+ end
120
+
121
+ include NodeFactory
122
+
123
+ # The base class for all condition nodes.
124
+ class Node
125
+ include NodeFactory
126
+ include NodeOperatorFactory
127
+
128
+ # Determines if the given path matches the conditions defined by this node
129
+ # and it's children.
130
+ #
131
+ # @param [String] path The path to match.
132
+ # @return [Boolean] true if +path+ matches, false otherwise.
133
+ def matches?(path)
134
+ false
135
+ end
136
+ end
137
+
138
+ # The base class for all nodes that perform binary operations (i.e.
139
+ # operations that take two operands).
140
+ #
141
+ # @!attribute [r] left
142
+ # @return [Node] the left child.
143
+ # @!attribute [r] right
144
+ # @return [Node] the right child.
145
+ class BinaryNode < Node
146
+ attr_reader :left, :right
147
+
148
+ # Creates a new binary node with left and right children.
149
+ #
150
+ # @param [Node] left The left child.
151
+ # @param [Node] right The right child.
152
+ def initialize(left, right)
153
+ @left = left
154
+ @right = right
155
+ end
156
+ end
157
+
158
+ # The base class for all nodes that perform unary operations (i.e.
159
+ # operations that take only one operand).
160
+ #
161
+ # @!attribute [r] child
162
+ # @return [Node] the child node.
163
+ class UnaryNode < Node
164
+ attr_reader :child
165
+
166
+ # Creates a new unary node.
167
+ #
168
+ # @param [Node] child The child.
169
+ def initialize(child)
170
+ @child = child
171
+ end
172
+ end
173
+
174
+ # A logical "and".
175
+ class AndNode < BinaryNode
176
+ # Determines if the given path matches the left AND the right child's
177
+ # conditions.
178
+ #
179
+ # @param [String] path The path to match.
180
+ # @return [Boolean] true if both the left and right children match
181
+ # +path+, false otherwise.
182
+ def matches?(path)
183
+ left.matches?(path) && right.matches?(path)
184
+ end
185
+
186
+ # Generates a string representation of this node.
187
+ #
188
+ # @return [String]
189
+ def to_s
190
+ "(#{left.to_s} AND #{right.to_s})"
191
+ end
192
+ end
193
+
194
+ # A logical "OR".
195
+ class OrNode < BinaryNode
196
+ # Determines if the given path matches the left OR the right child's
197
+ # conditions.
198
+ #
199
+ # @param [String] path The path to match.
200
+ # @return [Boolean] true if the left or the right child matches +path+,
201
+ # false otherwise.
202
+ def matches?(path)
203
+ left.matches?(path) || right.matches?(path)
204
+ end
205
+
206
+ # Generates a string representation of this node.
207
+ #
208
+ # @return [String]
209
+ def to_s
210
+ "(#{left.to_s} OR #{right.to_s})"
211
+ end
212
+ end
213
+
214
+ # A logical "NOT".
215
+ class NotNode < UnaryNode
216
+ # Determines if the given path does NOT match the child's conditions.
217
+ #
218
+ # @param [String] path The path to match.
219
+ # @return [Boolean] true if the child does not match +path+, false
220
+ # otherwise.
221
+ def matches?(path)
222
+ !child.matches?(path)
223
+ end
224
+
225
+ # Generates a string representation of this node.
226
+ #
227
+ # @return [String]
228
+ def to_s
229
+ "(NOT #{child.to_s})"
230
+ end
231
+ end
232
+
233
+ # Matches file extensions.
234
+ #
235
+ # @!attribute [r] extension
236
+ # @return [String] the extension to match.
237
+ class FileExtensionNode < Node
238
+ attr_reader :extension
239
+
240
+ # Creates a new file extension node.
241
+ #
242
+ # @param [String] extension The extension to match.
243
+ def initialize(extension)
244
+ @extension = extension
245
+ end
246
+
247
+ # Determines if the given path's file extension matches +extension+.
248
+ #
249
+ # @param [String] path The path to match.
250
+ # @return [Boolean] true if the path matches +extension+, false otherwise.
251
+ def matches?(path)
252
+ # avoid using File.extname to allow matching against double extensions,
253
+ # eg. file.html.erb
254
+ path[-extension.size..-1] == extension
255
+ end
256
+
257
+ # Generates a string representation of this node.
258
+ #
259
+ # @return [String]
260
+ def to_s
261
+ "has_file_extension('#{extension}')"
262
+ end
263
+ end
264
+
265
+ # Matches paths.
266
+ #
267
+ # @!attribute [r] path
268
+ # @return [String] the path to match.
269
+ class PathNode < Node
270
+ attr_reader :path
271
+
272
+ # Creates a new path node.
273
+ #
274
+ # @param [String] path The path to match.
275
+ def initialize(path)
276
+ @path = path
277
+ end
278
+
279
+ # Determines if the given path matches +path+.
280
+ #
281
+ # @param [String] match_path The path to match.
282
+ # @return [Boolean] true if +match_path+ matches +path+, false otherwise.
283
+ # Matching is done by comparing the first +n+ characters of +match_path+
284
+ # to +path+, where +n+ is the number of characters in +path+. In other words,
285
+ # if +path+ is "/path/to" and +match_path+ is '/path/to/foo.rb', this method
286
+ # will return true.
287
+ def matches?(match_path)
288
+ match_path[0...path.size] == path
289
+ end
290
+
291
+ # Generates a string representation of this node.
292
+ #
293
+ # @return [String]
294
+ def to_s
295
+ "matches_path('#{path}')"
296
+ end
297
+ end
298
+
299
+ # Determines if the given path matches +regex+.
300
+ #
301
+ # @!attribute [r] regex
302
+ # @return [Regex] The regex to match with.
303
+ class RegexNode < Node
304
+ attr_reader :regex
305
+
306
+ # Creates a new regex node.
307
+ #
308
+ # @param [Regex] regex The regex to match with.
309
+ def initialize(regex)
310
+ @regex = regex
311
+ end
312
+
313
+ # Determines if +regex+ matches the given path.
314
+ #
315
+ # @param [String] path The path to match.
316
+ # @return [Boolean] true if +regex+ matches +path+, false otherwise.
317
+ def matches?(path)
318
+ !!(path =~ regex)
319
+ end
320
+
321
+ # Generates a string representation of this node.
322
+ #
323
+ # @return [String]
324
+ def to_s
325
+ "matches_regex(/#{regex.source}/)"
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: UTF-8
2
+
3
+ module Rosette
4
+ module Core
5
+
6
+ # Logic for handling extractor ids. Extractor ids are strings that refer to
7
+ # a particular extractor class. For example, the id 'yaml/rails' refers to
8
+ # +Rosette::Extractors::YamlExtractor::RailsExtractor+.
9
+ #
10
+ # @example
11
+ # ExtractorId.resolve('yaml/rails')
12
+ # # => Rosette::Extractors::YamlExtractor::RailsExtractor
13
+ class ExtractorId < Resolver
14
+ class << self
15
+
16
+ # Parses and identifies the class constant for the given extractor id.
17
+ #
18
+ # @param [Class, String] extractor_id When given a class, returns the
19
+ # class. When given a string, parses and identifies the corresponding
20
+ # class constant in +namespace+.
21
+ # @param [Class] namespace The namespace to look in.
22
+ # @return [Class] The identified class constant.
23
+ def resolve(extractor_id, namespace = Rosette::Extractors)
24
+ super
25
+ end
26
+
27
+ private
28
+
29
+ def suffix
30
+ 'Extractor'
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: UTF-8
2
+
3
+ module Rosette
4
+ module Core
5
+
6
+ # Logic for handling integration ids. Integration ids are strings that refer
7
+ # to a particular integration class. For example, the id 'smartling' refers
8
+ # to +Rosette::Integrations::SmartlingIntegration+.
9
+ #
10
+ # @example
11
+ # IntegrationId.resolve('smartling')
12
+ # # => Rosette::Integrations::SmartlingIntegration
13
+ class IntegrationId < Resolver
14
+ class << self
15
+
16
+ # Parses and identifies the class constant for the given integration id.
17
+ #
18
+ # @param [Class, String] integration_id When given a class, returns the
19
+ # class. When given a string, parses and identifies the corresponding
20
+ # class constant in +namespace+.
21
+ # @param [Class] namespace The namespace to look in.
22
+ # @return [Class] The identified class constant.
23
+ def resolve(integration_id, namespace = Rosette::Integrations)
24
+ super
25
+ end
26
+
27
+ private
28
+
29
+ def suffix
30
+ 'Integration'
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+
3
+ module Rosette
4
+ module Core
5
+
6
+ # Logic for handling preprocessor ids. Preprocessor ids are strings that
7
+ # refer to a particular preprocessor class. For example, the id
8
+ # 'normalization' refers to
9
+ # +Rosette::Preprocessors::NormalizationPreprocessor+.
10
+ #
11
+ # @example
12
+ # PreprocessorId.resolve('normalization')
13
+ # # => Rosette::Preprocessors::NormalizationPreprocessor
14
+ class PreprocessorId < Resolver
15
+ class << self
16
+
17
+ # Parses and identifies the class constant for the given preprocessor id.
18
+ #
19
+ # @param [Class, String] preprocessor_id When given a class, returns the
20
+ # class. When given a string, parses and identifies the corresponding
21
+ # class constant in +namespace+.
22
+ # @param [Class] namespace The namespace to look in.
23
+ # @return [Class] The identified class constant.
24
+ def resolve(preprocessor_id, namespace = Rosette::Preprocessors)
25
+ super
26
+ end
27
+
28
+ private
29
+
30
+ def suffix
31
+ 'Preprocessor'
32
+ end
33
+
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,115 @@
1
+ # encoding: UTF-8
2
+
3
+ module Rosette
4
+ module Core
5
+
6
+ # Base class for Rosette's id resolvers that can look up a class constant
7
+ # given a namespaced id (string separated by forward slashes). For example,
8
+ # the extractor id "yaml/rails" resolves to
9
+ # +Rosette::Extractors::YamlExtractor::RailsExtractor+.
10
+ #
11
+ # @example
12
+ # class MyResolver < Resolver
13
+ # def resolve(id, namespace = MyNamespace::Foo)
14
+ # super
15
+ # end
16
+ #
17
+ # private
18
+ #
19
+ # # Must be defined by classes that inherit from Resolver.
20
+ # def suffix
21
+ # 'Stuff'
22
+ # end
23
+ # end
24
+ #
25
+ # module MyNamespace
26
+ # module Foo
27
+ # module BarStuff
28
+ # class BazStuff
29
+ # ...
30
+ # end
31
+ # end
32
+ # end
33
+ # end
34
+ #
35
+ # MyResolver.resolve('bar/baz') # => MyNamespace::Foo::BarStuff::BazStuff
36
+ class Resolver
37
+ class << self
38
+
39
+ # Parses and identifies the class constant for the given id.
40
+ #
41
+ # @param [Class, String] id When given a class, returns the class. When
42
+ # given a string, parses and identifies the corresponding class
43
+ # constant in +namespace+.
44
+ # @param [Class] namespace The namespace to look in.
45
+ # @return [Class] The identified class constant.
46
+ def resolve(id, namespace)
47
+ klass = case id
48
+ when Class
49
+ id
50
+ when String
51
+ lookup(id, namespace)
52
+ end
53
+
54
+ unless klass
55
+ raise ArgumentError, "#{id} could not be found - have you required it?"
56
+ end
57
+
58
+ klass
59
+ end
60
+
61
+ # Splits an id into parts.
62
+ #
63
+ # @param [String] id The id to parse.
64
+ # @return [Array<String>] A list of id parts.
65
+ def parse_id(id)
66
+ id.split('/')
67
+ end
68
+
69
+ private
70
+
71
+ def lookup(id, namespace)
72
+ find_const(
73
+ const_candidates(
74
+ parse_id(id).map do |segment|
75
+ StringUtils.camelize(segment)
76
+ end
77
+ ), namespace
78
+ )
79
+ end
80
+
81
+ # Appends the suffix to each segment one at a time and returns intermediate
82
+ # arrays of segments. For example, if given ['Json', 'KeyValue'] and a suffix
83
+ # of 'Serializer', this method would return:
84
+ # [['Json', 'KeyValue'], ['Json', 'KeyValueSerializer'], ['JsonSerializer', 'KeyValueSerializer']]
85
+ def const_candidates(segments)
86
+ [segments] + segments.map.with_index do |segment, idx|
87
+ candidate = segments[0...(segments.size - (idx + 1))]
88
+ candidate + segments[(segments.size - (idx + 1))..-1].map do |sub_seg|
89
+ "#{sub_seg}#{suffix}"
90
+ end
91
+ end
92
+ end
93
+
94
+ def suffix
95
+ raise NotImplementedError, "#{__method__} must be defined in derived classes"
96
+ end
97
+
98
+ def find_const(candidates, namespace)
99
+ candidates.each do |segments|
100
+ found_const = segments.inject(namespace) do |const, segment|
101
+ if const && const.const_defined?(segment)
102
+ const.const_get(segment)
103
+ end
104
+ end
105
+
106
+ return found_const if found_const
107
+ end
108
+ nil
109
+ end
110
+
111
+ end
112
+ end
113
+
114
+ end
115
+ end