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,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