nucleon 0.1.10 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.10
1
+ 0.1.11
@@ -14,7 +14,7 @@ class Core < Config
14
14
  #-----------------------------------------------------------------------------
15
15
  # Constructor / Destructor
16
16
 
17
- def initialize(data = {}, defaults = {}, force = true)
17
+ def initialize(data = {}, defaults = {}, force = true, set_initialized = true)
18
18
  super(data, defaults, force)
19
19
 
20
20
  @class_color = Util::Data.ensure_value(delete(:class_color, :cyan), :cyan)
@@ -24,6 +24,14 @@ class Core < Config
24
24
  self.ui = Config.new(export).defaults({ :resource => Util::Console.colorize(@class_label, @class_color) })
25
25
 
26
26
  logger.debug('Initialized instance logger and interface')
27
+ @initialized = true if set_initialized
28
+ end
29
+
30
+ #-----------------------------------------------------------------------------
31
+ # Checks
32
+
33
+ def initialized?
34
+ @initialized
27
35
  end
28
36
 
29
37
  #-----------------------------------------------------------------------------
@@ -95,12 +95,13 @@ module PluginInterface
95
95
  data = get_hash(_plural) unless data
96
96
 
97
97
  providers = [ providers ] if providers && ! providers.is_a?(Array)
98
+ providers.collect! { |elem| elem.to_sym }
98
99
 
99
100
  logger.debug("Initializing #{_plugin_type} plugin data: #{data.inspect}")
100
101
  logger.debug("Providers: #{providers.inspect}")
101
102
 
102
103
  symbol_map(data).each do |provider, instance_settings|
103
- if ! providers || providers.include?(provider.to_s)
104
+ if ! providers || providers.include?(provider)
104
105
  if _single_instance
105
106
  logger.debug("Initializing single instance plugin: #{instance_settings.inspect}")
106
107
 
@@ -14,11 +14,13 @@ class Base < Core
14
14
  set_meta(config.delete(:meta, Config.new))
15
15
 
16
16
  # No logging statements aove this line!!
17
- super(config.import({ :logger => "#{plugin_type}->#{plugin_provider}" }))
17
+ super(config.import({ :logger => "#{plugin_type}->#{plugin_provider}" }), {}, true, false)
18
18
  myself.plugin_name = name
19
19
 
20
20
  logger.debug("Normalizing #{plugin_type} plugin #{plugin_name} with meta data: #{meta.inspect}")
21
21
  normalize(false)
22
+
23
+ @initialized = true
22
24
  end
23
25
 
24
26
  #---
@@ -30,12 +32,6 @@ class Base < Core
30
32
  #-----------------------------------------------------------------------------
31
33
  # Checks
32
34
 
33
- def initialized?(options = {})
34
- return true
35
- end
36
-
37
- #---
38
-
39
35
  def quiet?
40
36
  @quiet
41
37
  end
@@ -49,6 +49,11 @@ class Project < Base
49
49
  ui.resource = plugin_name
50
50
  logger = plugin_name
51
51
 
52
+ unless reload
53
+ @cache = Util::Cache.new(directory, Nucleon.sha1(plugin_name), '.project_cache')
54
+ init_cache
55
+ end
56
+
52
57
  if keys = delete(:keys, nil)
53
58
  set(:private_key, keys[:private_key])
54
59
  set(:public_key, keys[:public_key])
@@ -117,6 +122,12 @@ class Project < Base
117
122
  #-----------------------------------------------------------------------------
118
123
  # Property accessor / modifiers
119
124
 
125
+ def cache
126
+ @cache
127
+ end
128
+
129
+ #---
130
+
120
131
  def reference
121
132
  get(:reference, nil)
122
133
  end
@@ -298,6 +309,13 @@ class Project < Base
298
309
  #-----------------------------------------------------------------------------
299
310
  # Project operations
300
311
 
312
+ def init_cache
313
+ # Override in providers if needed
314
+ end
315
+ protected :init_cache
316
+
317
+ #---
318
+
301
319
  def init_auth
302
320
  if can_persist?
303
321
  localize do
@@ -443,6 +461,15 @@ class Project < Base
443
461
  success
444
462
  end
445
463
 
464
+ #---
465
+
466
+ def ignore(files)
467
+ return unless directory
468
+ files = nil
469
+ files = yield if block_given?
470
+ commit(files, { :message => "Adding project ignores." }) if files
471
+ end
472
+
446
473
  #-----------------------------------------------------------------------------
447
474
  # Subproject operations
448
475
 
@@ -0,0 +1,149 @@
1
+
2
+ module Nucleon
3
+ module Util
4
+ class Cache < Core
5
+
6
+ @@cache_lock = Mutex.new
7
+
8
+ #-----------------------------------------------------------------------------
9
+
10
+ # This class already inherits much of what we need from the core config class.
11
+ # Right now we just have to worry about persistence
12
+
13
+ #-----------------------------------------------------------------------------
14
+ # Constructor / Destructor
15
+
16
+ def initialize(root_path, id, cache_dir = '.cache', force = true)
17
+ super({}, {}, force)
18
+
19
+ @cache_dir = cache_dir
20
+ @cache_root = File.join(root_path, cache_dir)
21
+ FileUtils.mkdir_p(base_path) unless File.directory?(base_path)
22
+
23
+ @cache_id = id.to_sym
24
+ @cache_translator = Nucleon.type_default(:translator)
25
+ @cache_filename = "#{id}.#{translator}"
26
+ @cache_path = File.join(@cache_root, @cache_filename)
27
+ end
28
+
29
+ #-----------------------------------------------------------------------------
30
+ # Property accessors / modifiers
31
+
32
+ def base_path
33
+ @cache_root
34
+ end
35
+
36
+ #---
37
+
38
+ def directory_name
39
+ @cache_dir
40
+ end
41
+
42
+ #---
43
+
44
+ def id
45
+ @cache_id
46
+ end
47
+
48
+ #---
49
+
50
+ def translator
51
+ @cache_translator
52
+ end
53
+
54
+ #---
55
+
56
+ def file
57
+ @cache_path
58
+ end
59
+
60
+ #---
61
+
62
+ def get(keys, default = nil, format = false)
63
+ result = super
64
+
65
+ if result.nil?
66
+ load
67
+ result = super
68
+ end
69
+ result
70
+ end
71
+
72
+ #---
73
+
74
+ def set(keys, value)
75
+ result = super
76
+ save if initialized?
77
+ result
78
+ end
79
+
80
+ #---
81
+
82
+ def delete(keys, default = nil)
83
+ result = super
84
+ save if initialized?
85
+ result
86
+ end
87
+
88
+ #---
89
+
90
+ def clear
91
+ result = super
92
+ save if initialized?
93
+ result
94
+ end
95
+
96
+ #-----------------------------------------------------------------------------
97
+ # Operations
98
+
99
+ def import_base(properties, options = {})
100
+ config = Config.ensure(options)
101
+
102
+ result = super
103
+ save if initialized? && ! config.get(:no_save, false)
104
+ result
105
+ end
106
+
107
+ #---
108
+
109
+ def load
110
+ success = false
111
+
112
+ @@cache_lock.synchronize do
113
+ logger.info("Loading #{translator} translated cache from #{file}")
114
+
115
+ parser = CORL.translator({}, translator)
116
+ raw = Disk.read(file)
117
+
118
+ if parser && raw && ! raw.empty?
119
+ logger.debug("Cache file contents: #{raw}")
120
+ parse_properties = Data.hash(parser.parse(raw))
121
+
122
+ import(parse_properties, { :no_save => true }) unless parse_properties.empty?
123
+ success = true
124
+ end
125
+ end
126
+ success
127
+ end
128
+ protected :load
129
+
130
+ #---
131
+
132
+ def save
133
+ success = false
134
+
135
+ @@cache_lock.synchronize do
136
+ if renderer = CORL.translator({}, translator)
137
+ rendering = renderer.generate(export)
138
+
139
+ if Disk.write(file, rendering)
140
+ success = true
141
+ end
142
+ end
143
+ end
144
+ success
145
+ end
146
+ protected :save
147
+ end
148
+ end
149
+ end
@@ -11,6 +11,8 @@ class Disk
11
11
  @@separator = false
12
12
  @@description = ''
13
13
 
14
+ @@file_lock = Mutex.new
15
+
14
16
  #-----------------------------------------------------------------------------
15
17
  # Utilities
16
18
 
@@ -36,13 +38,15 @@ class Disk
36
38
  reset = true if ! mode.empty? && mode != @@files[file_name][:mode]
37
39
  end
38
40
 
39
- if ! @@files.has_key?(file_name) || ! @@files[file_name][:file] || reset
40
- @@files[file_name][:file].close if @@files[file_name] && @@files[file_name][:file]
41
- unless mode.empty? || ( mode == 'r' && ! ::File.exists?(file_name) )
42
- @@files[file_name] = {
43
- :file => ::File.open(file_name, mode),
44
- :mode => mode,
45
- }
41
+ @@file_lock.synchronize do
42
+ if ! @@files.has_key?(file_name) || ! @@files[file_name][:file] || reset
43
+ @@files[file_name][:file].close if @@files[file_name] && @@files[file_name][:file]
44
+ unless mode.empty? || ( mode == 'r' && ! ::File.exists?(file_name) )
45
+ @@files[file_name] = {
46
+ :file => ::File.open(file_name, mode),
47
+ :mode => mode,
48
+ }
49
+ end
46
50
  end
47
51
  end
48
52
  return nil unless @@files[file_name]
@@ -52,14 +56,17 @@ class Disk
52
56
  #---
53
57
 
54
58
  def self.read(file_name, options = {})
59
+ result = nil
55
60
  options[:mode] = ( options[:mode] ? options[:mode] : 'r' )
56
61
  file = open(file_name, options)
57
62
 
58
63
  if file
59
- file.pos = 0 if options[:mode] == 'r'
60
- return file.read
64
+ @@file_lock.synchronize do
65
+ file.pos = 0 if options[:mode] == 'r'
66
+ result = file.read
67
+ end
61
68
  end
62
- return nil
69
+ return result
63
70
  end
64
71
 
65
72
  #---
@@ -67,23 +74,29 @@ class Disk
67
74
  def self.write(file_name, data, options = {})
68
75
  options[:mode] = ( options[:mode] ? options[:mode] : 'w' )
69
76
  file = open(file_name, options)
77
+ result = nil
70
78
 
71
79
  if file
72
- file.pos = 0 if options[:mode] == 'w'
73
- success = file.write(data)
74
- begin
75
- file.flush
76
- rescue # In case the file is already closed
80
+ @@file_lock.synchronize do
81
+ file.pos = 0 if options[:mode] == 'w'
82
+ result = file.write(data)
83
+ begin
84
+ file.flush
85
+ rescue # In case the file is already closed
86
+ end
77
87
  end
78
- return success
79
88
  end
80
- return nil
89
+ return result
81
90
  end
82
91
 
83
92
  #---
84
93
 
85
94
  def self.delete(file_path)
86
- return ::File.delete(file_path)
95
+ result = nil
96
+ @@file_lock.synchronize do
97
+ result = ::File.delete(file_path)
98
+ end
99
+ result
87
100
  end
88
101
 
89
102
  #---
@@ -91,10 +104,12 @@ class Disk
91
104
  def self.log(data, options = {})
92
105
  reset = ( options[:file_name] || options[:mode] )
93
106
  file = open(( options[:file_name] ? options[:file_name] : 'log.txt' ), options, reset)
94
- if file
95
- file.write("--------------------------------------\n") if @@separator
96
- file.write("#{@@description}\n") if @@description
97
- file.write("#{data}\n")
107
+ if file
108
+ @@file_lock.synchronize do
109
+ file.write("--------------------------------------\n") if @@separator
110
+ file.write("#{@@description}\n") if @@description
111
+ file.write("#{data}\n")
112
+ end
98
113
  end
99
114
  end
100
115
 
@@ -108,8 +123,10 @@ class Disk
108
123
  end
109
124
 
110
125
  file_names.each do |file_name|
111
- @@files[file_name][:file].close if @@files[file_name] && @@files[file_name][:file]
112
- @@files.delete(file_name)
126
+ @@file_lock.synchronize do
127
+ @@files[file_name][:file].close if @@files[file_name] && @@files[file_name][:file]
128
+ @@files.delete(file_name)
129
+ end
113
130
  end
114
131
  end
115
132
  end
@@ -10,7 +10,7 @@ class Liquid
10
10
  #---
11
11
 
12
12
  def method_missing(method, *args, &block)
13
- @code.call(method, args, block)
13
+ @code.call(method, args, &block)
14
14
  end
15
15
  end
16
16
  end
@@ -16,11 +16,11 @@ class Shell < Core
16
16
  attr_accessor :status
17
17
  attr_reader :command
18
18
 
19
- def initialize(command)
19
+ def initialize(command, status = nil)
20
20
  @command = command
21
21
  @output = ''
22
22
  @errors = ''
23
- @status = Nucleon.code.success
23
+ @status = status.nil? ? Nucleon.code.success : status
24
24
  end
25
25
 
26
26
  #---
@@ -195,10 +195,26 @@ class Git < Plugin::Project
195
195
  result
196
196
  end
197
197
  end
198
-
198
+
199
+ #-----------------------------------------------------------------------------
200
+ # Operations
201
+
202
+ def init_cache
203
+ ignore(cache.directory_name)
204
+ end
205
+
199
206
  #-----------------------------------------------------------------------------
200
207
  # Basic Git operations
201
208
 
209
+ def ignore(files)
210
+ super do
211
+ ensure_in_gitignore(files)
212
+ '.gitignore'
213
+ end
214
+ end
215
+
216
+ #---
217
+
202
218
  def load_revision
203
219
  return super do
204
220
  if new?
@@ -529,6 +545,35 @@ class Git < Plugin::Project
529
545
  result
530
546
  end
531
547
  protected :git_exec
548
+
549
+ #---
550
+
551
+ def ensure_in_gitignore(files)
552
+ files = [ files ] unless files.is_a?(Array)
553
+ changes = false
554
+
555
+ gitignore_file = File.join(directory, '.gitignore')
556
+ ignore_raw = Util::Disk.read(gitignore_file)
557
+ ignores = []
558
+ ignores = ignore_raw.split("\n") if ignore_raw && ! ignore_raw.empty?
559
+
560
+ files.each do |file|
561
+ found = false
562
+ unless ignores.empty?
563
+ ignores.each do |ignore|
564
+ if ignore.strip.match(/^#{file}\/?$/)
565
+ found = true
566
+ end
567
+ end
568
+ end
569
+ unless found
570
+ ignores << file
571
+ changes = true
572
+ end
573
+ end
574
+ Util::Disk.write(gitignore_file, ignores.join("\n")) if changes
575
+ end
576
+ protected :ensure_in_gitignore
532
577
  end
533
578
  end
534
579
  end
@@ -164,7 +164,8 @@ nucleon_require(core_dir, :core)
164
164
  [ :liquid,
165
165
  :cli,
166
166
  :disk,
167
- :package,
167
+ :package,
168
+ :cache,
168
169
  :shell,
169
170
  :ssh
170
171
  ].each do |name|
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "nucleon"
8
- s.version = "0.1.10"
8
+ s.version = "0.1.11"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Adrian Webb"]
12
- s.date = "2014-03-28"
12
+ s.date = "2014-04-02"
13
13
  s.description = "\nA framework that provides a simple foundation for building Ruby applications that are:\n\n* Highly configurable (with both distributed and persistent configurations)\n* Extremely pluggable and extendable\n* Easily parallel\n\nNote: This framework is still very early in development!\n"
14
14
  s.email = "adrian.webb@coralnexus.com"
15
15
  s.executables = ["nucleon"]
@@ -54,6 +54,7 @@ Gem::Specification.new do |s|
54
54
  "lib/core/plugin/project.rb",
55
55
  "lib/core/plugin/template.rb",
56
56
  "lib/core/plugin/translator.rb",
57
+ "lib/core/util/cache.rb",
57
58
  "lib/core/util/cli.rb",
58
59
  "lib/core/util/console.rb",
59
60
  "lib/core/util/data.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nucleon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.1.11
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-28 00:00:00.000000000 Z
12
+ date: 2014-04-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: log4r
16
- requirement: &12902440 !ruby/object:Gem::Requirement
16
+ requirement: &23185960 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *12902440
24
+ version_requirements: *23185960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: i18n
27
- requirement: &12901680 !ruby/object:Gem::Requirement
27
+ requirement: &23185380 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0.6'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *12901680
35
+ version_requirements: *23185380
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: netrc
38
- requirement: &12900600 !ruby/object:Gem::Requirement
38
+ requirement: &23184460 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0.7'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *12900600
46
+ version_requirements: *23184460
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: deep_merge
49
- requirement: &13008240 !ruby/object:Gem::Requirement
49
+ requirement: &23183740 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '1.0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *13008240
57
+ version_requirements: *23183740
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: multi_json
60
- requirement: &13007120 !ruby/object:Gem::Requirement
60
+ requirement: &23183200 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '1.7'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *13007120
68
+ version_requirements: *23183200
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: sshkey
71
- requirement: &13006360 !ruby/object:Gem::Requirement
71
+ requirement: &23182520 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '1.6'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *13006360
79
+ version_requirements: *23182520
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: childprocess
82
- requirement: &13005560 !ruby/object:Gem::Requirement
82
+ requirement: &23181760 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: 0.5.0
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *13005560
90
+ version_requirements: *23181760
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: celluloid
93
- requirement: &13004820 !ruby/object:Gem::Requirement
93
+ requirement: &23181040 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ~>
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0.15'
99
99
  type: :runtime
100
100
  prerelease: false
101
- version_requirements: *13004820
101
+ version_requirements: *23181040
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: rugged
104
- requirement: &13004340 !ruby/object:Gem::Requirement
104
+ requirement: &23180540 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ~>
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0.19'
110
110
  type: :runtime
111
111
  prerelease: false
112
- version_requirements: *13004340
112
+ version_requirements: *23180540
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: octokit
115
- requirement: &13003800 !ruby/object:Gem::Requirement
115
+ requirement: &23179980 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ~>
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: '2.7'
121
121
  type: :runtime
122
122
  prerelease: false
123
- version_requirements: *13003800
123
+ version_requirements: *23179980
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: bundler
126
- requirement: &13003000 !ruby/object:Gem::Requirement
126
+ requirement: &23253080 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ~>
@@ -131,10 +131,10 @@ dependencies:
131
131
  version: '1.2'
132
132
  type: :development
133
133
  prerelease: false
134
- version_requirements: *13003000
134
+ version_requirements: *23253080
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: jeweler
137
- requirement: &13002380 !ruby/object:Gem::Requirement
137
+ requirement: &23251720 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ~>
@@ -142,10 +142,10 @@ dependencies:
142
142
  version: '2.0'
143
143
  type: :development
144
144
  prerelease: false
145
- version_requirements: *13002380
145
+ version_requirements: *23251720
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: rspec
148
- requirement: &13001880 !ruby/object:Gem::Requirement
148
+ requirement: &23250760 !ruby/object:Gem::Requirement
149
149
  none: false
150
150
  requirements:
151
151
  - - ~>
@@ -153,10 +153,10 @@ dependencies:
153
153
  version: '2.10'
154
154
  type: :development
155
155
  prerelease: false
156
- version_requirements: *13001880
156
+ version_requirements: *23250760
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: rdoc
159
- requirement: &13001400 !ruby/object:Gem::Requirement
159
+ requirement: &23249360 !ruby/object:Gem::Requirement
160
160
  none: false
161
161
  requirements:
162
162
  - - ~>
@@ -164,10 +164,10 @@ dependencies:
164
164
  version: '3.12'
165
165
  type: :development
166
166
  prerelease: false
167
- version_requirements: *13001400
167
+ version_requirements: *23249360
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: yard
170
- requirement: &13097020 !ruby/object:Gem::Requirement
170
+ requirement: &23248640 !ruby/object:Gem::Requirement
171
171
  none: false
172
172
  requirements:
173
173
  - - ~>
@@ -175,7 +175,7 @@ dependencies:
175
175
  version: '0.8'
176
176
  type: :development
177
177
  prerelease: false
178
- version_requirements: *13097020
178
+ version_requirements: *23248640
179
179
  description: ! '
180
180
 
181
181
  A framework that provides a simple foundation for building Ruby applications that
@@ -236,6 +236,7 @@ files:
236
236
  - lib/core/plugin/project.rb
237
237
  - lib/core/plugin/template.rb
238
238
  - lib/core/plugin/translator.rb
239
+ - lib/core/util/cache.rb
239
240
  - lib/core/util/cli.rb
240
241
  - lib/core/util/console.rb
241
242
  - lib/core/util/data.rb