fusuma 1.11.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +49 -7
  3. data/fusuma.gemspec +6 -16
  4. data/lib/fusuma.rb +91 -28
  5. data/lib/fusuma/config.rb +34 -62
  6. data/lib/fusuma/config/index.rb +39 -6
  7. data/lib/fusuma/config/searcher.rb +166 -0
  8. data/lib/fusuma/custom_process.rb +13 -0
  9. data/lib/fusuma/device.rb +22 -7
  10. data/lib/fusuma/environment.rb +6 -4
  11. data/lib/fusuma/hash_support.rb +40 -0
  12. data/lib/fusuma/libinput_command.rb +17 -21
  13. data/lib/fusuma/multi_logger.rb +2 -6
  14. data/lib/fusuma/plugin/base.rb +18 -15
  15. data/lib/fusuma/plugin/buffers/buffer.rb +3 -2
  16. data/lib/fusuma/plugin/buffers/gesture_buffer.rb +34 -25
  17. data/lib/fusuma/plugin/buffers/timer_buffer.rb +46 -0
  18. data/lib/fusuma/plugin/detectors/detector.rb +26 -5
  19. data/lib/fusuma/plugin/detectors/pinch_detector.rb +109 -58
  20. data/lib/fusuma/plugin/detectors/rotate_detector.rb +91 -50
  21. data/lib/fusuma/plugin/detectors/swipe_detector.rb +93 -56
  22. data/lib/fusuma/plugin/events/event.rb +5 -4
  23. data/lib/fusuma/plugin/events/records/context_record.rb +27 -0
  24. data/lib/fusuma/plugin/events/records/gesture_record.rb +9 -6
  25. data/lib/fusuma/plugin/events/records/index_record.rb +46 -14
  26. data/lib/fusuma/plugin/events/records/record.rb +1 -1
  27. data/lib/fusuma/plugin/events/records/text_record.rb +2 -1
  28. data/lib/fusuma/plugin/executors/command_executor.rb +21 -6
  29. data/lib/fusuma/plugin/executors/executor.rb +45 -3
  30. data/lib/fusuma/plugin/filters/filter.rb +1 -1
  31. data/lib/fusuma/plugin/filters/libinput_device_filter.rb +6 -7
  32. data/lib/fusuma/plugin/filters/libinput_timeout_filter.rb +2 -2
  33. data/lib/fusuma/plugin/inputs/input.rb +64 -8
  34. data/lib/fusuma/plugin/inputs/libinput_command_input.rb +19 -9
  35. data/lib/fusuma/plugin/inputs/timer_input.rb +63 -0
  36. data/lib/fusuma/plugin/manager.rb +22 -29
  37. data/lib/fusuma/plugin/parsers/libinput_gesture_parser.rb +10 -8
  38. data/lib/fusuma/plugin/parsers/parser.rb +8 -9
  39. data/lib/fusuma/string_support.rb +16 -0
  40. data/lib/fusuma/version.rb +1 -1
  41. data/spec/helpers/config_helper.rb +20 -0
  42. data/spec/lib/config/searcher_spec.rb +97 -0
  43. data/spec/lib/config_spec.rb +112 -0
  44. data/spec/lib/custom_process_spec.rb +28 -0
  45. data/spec/lib/device_spec.rb +98 -0
  46. data/spec/lib/dummy_config.yml +31 -0
  47. data/spec/lib/fusuma_spec.rb +103 -0
  48. data/spec/lib/libinput-list-devices_iberianpig-XPS-9360.txt +181 -0
  49. data/spec/lib/libinput-list-devices_magic_trackpad.txt +51 -0
  50. data/spec/lib/libinput-list-devices_razer_razer_blade.txt +252 -0
  51. data/spec/lib/libinput-list-devices_thejinx0r.txt +361 -0
  52. data/spec/lib/libinput-list-devices_unavailable.txt +36 -0
  53. data/spec/lib/libinput_command_spec.rb +167 -0
  54. data/spec/lib/plugin/base_spec.rb +74 -0
  55. data/spec/lib/plugin/buffers/buffer_spec.rb +80 -0
  56. data/spec/lib/plugin/buffers/dummy_buffer.rb +20 -0
  57. data/spec/lib/plugin/buffers/gesture_buffer_spec.rb +172 -0
  58. data/spec/lib/plugin/detectors/detector_spec.rb +43 -0
  59. data/spec/lib/plugin/detectors/dummy_detector.rb +24 -0
  60. data/spec/lib/plugin/detectors/pinch_detector_spec.rb +119 -0
  61. data/spec/lib/plugin/detectors/rotate_detector_spec.rb +125 -0
  62. data/spec/lib/plugin/detectors/swipe_detector_spec.rb +118 -0
  63. data/spec/lib/plugin/events/event_spec.rb +30 -0
  64. data/spec/lib/plugin/events/records/gesture_record_spec.rb +22 -0
  65. data/spec/lib/plugin/events/records/record_spec.rb +31 -0
  66. data/spec/lib/plugin/events/records/text_record_spec.rb +26 -0
  67. data/spec/lib/plugin/executors/command_executor_spec.rb +57 -0
  68. data/spec/lib/plugin/executors/executor_spec.rb +160 -0
  69. data/spec/lib/plugin/filters/filter_spec.rb +92 -0
  70. data/spec/lib/plugin/filters/libinput_filter_spec.rb +120 -0
  71. data/spec/lib/plugin/inputs/input_spec.rb +70 -0
  72. data/spec/lib/plugin/inputs/libinput_command_input_spec.rb +120 -0
  73. data/spec/lib/plugin/inputs/timer_input_spec.rb +40 -0
  74. data/spec/lib/plugin/manager_spec.rb +27 -0
  75. data/spec/lib/plugin/parsers/parser_spec.rb +45 -0
  76. data/spec/spec_helper.rb +20 -0
  77. metadata +90 -167
  78. data/.github/FUNDING.yml +0 -8
  79. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -32
  80. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -17
  81. data/.github/pull_request_template.md +0 -9
  82. data/.github/stale.yml +0 -18
  83. data/.gitignore +0 -17
  84. data/.reek.yml +0 -96
  85. data/.rspec +0 -2
  86. data/.rubocop.yml +0 -37
  87. data/.rubocop_todo.yml +0 -40
  88. data/.travis.yml +0 -11
  89. data/CHANGELOG.md +0 -456
  90. data/CODE_OF_CONDUCT.md +0 -74
  91. data/CONTRIBUTING.md +0 -72
  92. data/Gemfile +0 -6
  93. data/Rakefile +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd9b74b06494e85c0caf6250e0dfeed80c37187098982b4f32c2dd8e727732fb
4
- data.tar.gz: eb8c3d19693570b77b26519d7296856480342eec3eba0205ed970e01a4b46a16
3
+ metadata.gz: 3d8572f753f134496b8ffc0f36c3aafbf142357aad913edd9fd5f5d494d32259
4
+ data.tar.gz: 6adf4afa35ca59305efa6bde88b2ccf808c3025b5a577cc917d0b32d6b794058
5
5
  SHA512:
6
- metadata.gz: abe0b55ab9f332d3f67c423b0890135973d57a4cf8b7d6fafc7b01b44874c2489b6752fcb7a66d0b0a625c612879e35ce7d11b6a3b5ce485711b84e8f45e5279
7
- data.tar.gz: 323c35769c6f9827f8a94532a4dd10a25facf6bc6858d85b2b6540a6d78064e9003d4ccb83f4914f214aa6523cea8878b5f63abbe113211b1b0900ce0e203521
6
+ metadata.gz: 68dbe15577eb01891537a573c96ab0a2c532e919588c4fdb62899b236382630d8f575f913c9de282bf9a66b5ae8f91cc820067eedc7ecaa42e839f25d090afd8
7
+ data.tar.gz: cd74e943167bff03bf2eb1d3eaa9d170f28fd921c3736bcc1a981e49f0c7b755de10ecd8b9b6f314bce76377568441e04cd5228222cfe3c3afd32448569a754c
data/README.md CHANGED
@@ -17,7 +17,7 @@ This gem makes your linux able to recognize swipes or pinchs and assign commands
17
17
 
18
18
  ## Installation
19
19
 
20
- ### 1. Grant permission to read the touchpad device
20
+ ### Grant permission to read the touchpad device
21
21
 
22
22
  **IMPORTANT**: You **MUST** be a member of the **INPUT** group to read touchpad by Fusuma.
23
23
 
@@ -25,9 +25,15 @@ This gem makes your linux able to recognize swipes or pinchs and assign commands
25
25
  $ sudo gpasswd -a $USER input
26
26
  ```
27
27
 
28
- Then, You **MUST** **REBOOT** to assign this group.
28
+ Then, You apply the change with no logout or reboot.
29
29
 
30
- ### 2. Install libinput-tools
30
+ ```bash
31
+ $ newgrp input
32
+ ```
33
+
34
+ ### For Debian Based Distros (Ubuntu, Debian, Mint, Pop!OS)
35
+
36
+ #### 1. Install libinput-tools
31
37
 
32
38
  You need `libinput` release 1.0 or later.
33
39
 
@@ -35,7 +41,7 @@ You need `libinput` release 1.0 or later.
35
41
  $ sudo apt-get install libinput-tools
36
42
  ```
37
43
 
38
- ### 3. Install Ruby
44
+ #### 2. Install Ruby
39
45
 
40
46
  Fusuma runs in Ruby, so you must install it first.
41
47
 
@@ -43,13 +49,13 @@ Fusuma runs in Ruby, so you must install it first.
43
49
  $ sudo apt-get install ruby
44
50
  ```
45
51
 
46
- ### 4. Install Fusuma
52
+ #### 3. Install Fusuma
47
53
 
48
54
  ```bash
49
55
  $ sudo gem install fusuma
50
56
  ```
51
57
 
52
- ### 5. Install xdotool (optional)
58
+ #### 4. Install xdotool (optional)
53
59
 
54
60
  For sending shortcuts:
55
61
 
@@ -57,6 +63,42 @@ For sending shortcuts:
57
63
  $ sudo apt-get install xdotool
58
64
  ```
59
65
 
66
+ ### For Arch Based Distros (Manjaro, Arch)
67
+
68
+ #### 1. Install libinput.
69
+
70
+ You need `libinput` release 1.0 or later. This is most probably installed by default on Manjaro
71
+
72
+ ```z-h
73
+ $ sudo pacman -S libinput
74
+ ```
75
+
76
+ #### 2. Install Ruby
77
+
78
+ Fusuma runs in Ruby, so you must install it first.
79
+
80
+ ```zsh
81
+ $ sudo pacman -S ruby
82
+ ```
83
+
84
+ #### 3. Install Fusuma
85
+
86
+ **Note:** By default in Arch Linux, when running ```gem```, gems are installed per-user (into ```~/.gem/ruby/```), instead of system-wide (into ```/usr/lib/ruby/gems/```). This is considered the best way to manage gems on Arch, because otherwise they might interfere with gems installed by Pacman. (From Arch Wiki)
87
+
88
+ To install gems system-wide, see any of the methods listed on [Arch Wiki](https://wiki.archlinux.org/index.php/ruby#Installing_gems_system-wide)
89
+
90
+ ```zsh
91
+ $ sudo gem install fusuma
92
+ ```
93
+
94
+ #### 4. Install xdotool (optional)
95
+
96
+ For sending shortcuts:
97
+
98
+ ```zsh
99
+ $ sudo pacman -S xdotool
100
+ ```
101
+
60
102
  ### Touchpad not working in GNOME
61
103
 
62
104
  Ensure the touchpad events are being sent to the GNOME desktop by running the following command:
@@ -242,7 +284,7 @@ swipe:
242
284
  - [ydotool](https://github.com/ReimuNotMoe/ydotool)
243
285
  - Wayland compatible
244
286
  - Needs more maintainers.
245
- - Requires only replacing `xdotool` with `ydotool` in in fusuma conf.
287
+ - Requires only replacing `xdotool` with `ydotool` in fusuma conf.
246
288
 
247
289
  ## Options
248
290
 
data/fusuma.gemspec CHANGED
@@ -10,29 +10,19 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ['iberianpig']
11
11
  spec.email = ['yhkyky@gmail.com']
12
12
 
13
- spec.summary = 'Multitouch gestures with libinput dirver on X11, Linux'
13
+ spec.summary = 'Multitouch gestures with libinput driver, Linux'
14
14
  spec.description = 'Fusuma is multitouch gesture recognizer. This gem makes your touchpad on Linux able to recognize swipes or pinchs and assign command to them. Read installation on Github(https://github.com/iberianpig/fusuma#installation).'
15
15
  spec.homepage = 'https://github.com/iberianpig/fusuma'
16
16
  spec.license = 'MIT'
17
17
 
18
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
- f.match(%r{^(test|spec|features)/})
20
- end
18
+ spec.files = Dir.glob('{bin,lib,exe}/**/*') + %w[LICENSE README.md fusuma.gemspec]
19
+ spec.test_files = Dir.glob("{test,spec,features}/**/*")
21
20
  spec.bindir = 'exe'
22
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
22
  spec.require_paths = ['lib']
24
23
  spec.metadata['yard.run'] = 'yri' # use "yard" to build full HTML docs.
25
24
 
26
- spec.required_ruby_version = '>= 2.3' # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
27
- spec.add_development_dependency 'bundler'
28
- spec.add_development_dependency 'coveralls'
29
- spec.add_development_dependency 'github_changelog_generator', '~> 1.14'
30
- spec.add_development_dependency 'pry-byebug', '~> 3.4'
31
- spec.add_development_dependency 'pry-doc'
32
- spec.add_development_dependency 'pry-inline'
33
- spec.add_development_dependency 'rake', '~> 13.0'
34
- spec.add_development_dependency 'reek'
35
- spec.add_development_dependency 'rspec', '~> 3.0'
36
- spec.add_development_dependency 'rubocop'
37
- spec.add_development_dependency 'yard'
25
+ spec.required_ruby_version = '>= 2.5.1' # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
26
+ # support bionic (18.04LTS) 2.5.1
27
+ spec.add_dependency 'posix-spawn'
38
28
  end
data/lib/fusuma.rb CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  require_relative './fusuma/version'
4
4
  require_relative './fusuma/multi_logger'
5
- require_relative './fusuma/config.rb'
6
- require_relative './fusuma/environment.rb'
7
- require_relative './fusuma/device.rb'
8
- require_relative './fusuma/plugin/manager.rb'
5
+ require_relative './fusuma/config'
6
+ require_relative './fusuma/environment'
7
+ require_relative './fusuma/device'
8
+ require_relative './fusuma/plugin/manager'
9
9
 
10
10
  # this is top level module
11
11
  module Fusuma
@@ -16,6 +16,8 @@ module Fusuma
16
16
  set_trap
17
17
  read_options(option)
18
18
  instance = new
19
+ ## NOTE: Uncomment following line to measure performance
20
+ # instance.run_with_lineprof
19
21
  instance.run
20
22
  end
21
23
 
@@ -62,59 +64,120 @@ module Fusuma
62
64
  end
63
65
 
64
66
  def run
65
- # TODO: run with multi thread
66
- @inputs.first.run do |event|
67
- clear_expired_events
68
- filtered = filter(event) || next
69
- parsed = parse(filtered) || next
70
- buffered = buffer(parsed) || next
71
- detected = detect(buffered) || next
72
- merged = merge(detected) || next
73
- execute(merged)
67
+ loop { pipeline }
68
+ end
69
+
70
+ def pipeline
71
+ event = input || return
72
+ clear_expired_events
73
+ filtered = filter(event) || return
74
+ parsed = parse(filtered) || return
75
+ buffered = buffer(parsed) || return
76
+ detected = detect(buffered) || return
77
+ condition, context, event = merge(detected) || return
78
+ execute(condition, context, event)
79
+ end
80
+
81
+ # For performance monitoring
82
+ def run_with_lineprof(count: 1000)
83
+ require 'rblineprof'
84
+ require 'rblineprof-report'
85
+
86
+ profile = lineprof(%r{#{Pathname.new(__FILE__).parent}/.}) do
87
+ count.times { pipeline }
74
88
  end
89
+ LineProf.report(profile)
90
+ exit 0
75
91
  end
76
92
 
93
+ # @return [Plugin::Events::Event]
94
+ def input
95
+ Plugin::Inputs::Input.select(@inputs)
96
+ end
97
+
98
+ # @param [Plugin::Events::Event]
99
+ # @return [Plugin::Events::Event]
77
100
  def filter(event)
78
101
  event if @filters.any? { |f| f.filter(event) }
79
102
  end
80
103
 
104
+ # @param [Plugin::Events::Event]
105
+ # @return [Plugin::Events::Event]
81
106
  def parse(event)
82
107
  @parsers.reduce(event) { |e, p| p.parse(e) if e }
83
108
  end
84
109
 
110
+ # @param [Plugin::Events::Event]
111
+ # @return [Array<Plugin::Buffers::Buffer>]
112
+ # @return [NilClass]
85
113
  def buffer(event)
86
- @buffers.any? { |b| b.buffer(event) } && @buffers
114
+ @buffers.select { |b| b.buffer(event) }
87
115
  end
88
116
 
89
117
  # @param buffers [Array<Buffer>]
90
118
  # @return [Array<Event>]
91
119
  def detect(buffers)
92
- @detectors.each_with_object([]) do |detector, detected|
93
- if (event = detector.detect(buffers))
94
- detected << event
95
- end
120
+ matched_detectors = @detectors.select do |detector|
121
+ detector.watch? ||
122
+ buffers.any? { |b| detector.sources.include?(b.type) }
123
+ end
124
+
125
+ events = matched_detectors.each_with_object([]) do |detector, detected|
126
+ Array(detector.detect(@buffers)).each { |e| detected << e }
96
127
  end
128
+
129
+ return if events.empty?
130
+
131
+ events
97
132
  end
98
133
 
99
- # @param events [Array<Event>]
100
- # @return [Event] a Event merged all records from arguments
134
+ # @param events [Array<Plugin::Events::Event>]
135
+ # @return [Plugin::Events::Event] Event merged all records from arguments
101
136
  # @return [NilClass] when event is NOT given
102
137
  def merge(events)
103
- main_events, modifiers = events.partition { |event| event.record.mergable? }
104
- return nil unless (main_event = main_events.first)
138
+ index_events, context_events = events.partition { |event| event.record.type == :index }
139
+ main_events, modifiers = index_events.partition { |event| event.record.mergable? }
140
+ request_context = context_events.each_with_object({}) do |e, results|
141
+ results[e.record.name] = e.record.value
142
+ end
143
+ main_events.sort_by! { |e| e.record.trigger_priority }
144
+
145
+ condition = nil
146
+ matched_context = nil
147
+ event = main_events.find do |main_event|
148
+ matched_context = Config::Searcher.find_context(request_context) do
149
+ condition, index_record = Config::Searcher.find_condition do
150
+ main_event.record.merge(records: modifiers.map(&:record))
151
+ end
152
+ main_event if index_record
153
+ end
154
+ end
155
+ return if event.nil?
105
156
 
106
- main_event.record.merge(records: modifiers.map(&:record))
107
- main_event
157
+ [condition, matched_context, event]
108
158
  end
109
159
 
110
- def execute(event)
160
+ # @param event [Plugin::Events::Event]
161
+ def execute(condition, context, event)
111
162
  return unless event
112
163
 
113
- executor = @executors.find do |e|
114
- e.executable?(event)
164
+ # Find executable condition and executor
165
+ executor = Config::Searcher.with_context(context) do
166
+ Config::Searcher.with_condition(condition) do
167
+ @executors.find { |e| e.executable?(event) }
168
+ end
115
169
  end
116
170
 
117
- executor&.execute(event)
171
+ return if executor.nil?
172
+
173
+ # Check interval and execute
174
+ Config::Searcher.with_context(context) do
175
+ Config::Searcher.with_condition(condition) do
176
+ executor.enough_interval?(event) &&
177
+ executor.update_interval(event) &&
178
+ executor.execute(event)
179
+ end
180
+ end
118
181
  end
119
182
 
120
183
  def clear_expired_events
data/lib/fusuma/config.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './multi_logger.rb'
4
- require_relative './config/index.rb'
5
- require_relative './config/yaml_duplication_checker.rb'
3
+ require_relative './multi_logger'
4
+ require_relative './config/index'
5
+ require_relative './config/searcher'
6
+ require_relative './config/yaml_duplication_checker'
7
+ require_relative './hash_support'
6
8
  require 'singleton'
7
9
  require 'yaml'
8
10
 
@@ -11,13 +13,18 @@ module Fusuma
11
13
  # read keymap from yaml file
12
14
  class Config
13
15
  class NotFoundError < StandardError; end
16
+
14
17
  class InvalidFileError < StandardError; end
15
18
 
16
19
  include Singleton
17
20
 
18
21
  class << self
19
- def search(keys)
20
- instance.search(keys)
22
+ def search(index)
23
+ instance.search(index)
24
+ end
25
+
26
+ def find_execute_key(index)
27
+ instance.find_execute_key(index)
21
28
  end
22
29
 
23
30
  def custom_path=(new_path)
@@ -25,12 +32,11 @@ module Fusuma
25
32
  end
26
33
  end
27
34
 
28
- attr_reader :keymap
29
- attr_reader :custom_path
35
+ attr_reader :keymap, :custom_path, :searcher
30
36
 
31
37
  def initialize
38
+ @searcher = Searcher.new
32
39
  @custom_path = nil
33
- @cache = nil
34
40
  @keymap = nil
35
41
  end
36
42
 
@@ -40,15 +46,15 @@ module Fusuma
40
46
  end
41
47
 
42
48
  def reload
43
- @cache = nil
49
+ @searcher = Searcher.new
44
50
  path = find_filepath
45
51
  MultiLogger.info "reload config: #{path}"
46
52
  @keymap = validate(path)
47
53
  self
48
54
  end
49
55
 
50
- # @return [Hash]
51
- # @raise [InvalidError]
56
+ # @return [Hash] If check passes
57
+ # @raise [InvalidFileError] If check does not pass
52
58
  def validate(path)
53
59
  duplicates = []
54
60
  YAMLDuplicationChecker.check(File.read(path), path) do |ignored, duplicate|
@@ -57,33 +63,30 @@ module Fusuma
57
63
  end
58
64
  raise InvalidFileError, "Detect duplicate keys #{duplicates}" unless duplicates.empty?
59
65
 
60
- yaml = YAML.load_file(path)
61
-
62
- raise InvalidFileError, 'Invaid YAML file' unless yaml.is_a? Hash
63
-
64
- yaml.deep_symbolize_keys
66
+ yamls = YAML.load_stream(File.read(path)).compact
67
+ yamls.map(&:deep_symbolize_keys)
65
68
  rescue StandardError => e
66
69
  MultiLogger.error e.message
67
70
  raise InvalidFileError, e.message
68
71
  end
69
72
 
70
73
  # @param index [Index]
74
+ # @param context [Hash]
71
75
  def search(index)
72
- cache(index.cache_key) do
73
- index.keys.reduce(keymap) do |location, key|
74
- if location.is_a?(Hash)
75
- begin
76
- if key.skippable
77
- location.fetch(key.symbol, location)
78
- else
79
- location.fetch(key.symbol, nil)
80
- end
81
- end
82
- else
83
- location
84
- end
85
- end
86
- end
76
+ @searcher.search_with_cache(index, location: keymap)
77
+ end
78
+
79
+ # @param index [Config::Index]
80
+ # @return Symbol
81
+ def find_execute_key(index)
82
+ @execute_keys ||= Plugin::Executors::Executor.plugins.map do |executor|
83
+ executor.new.execute_keys
84
+ end.flatten
85
+
86
+ execute_params = search(index)
87
+ return if execute_params.nil?
88
+
89
+ @execute_keys.find { |k| execute_params.keys.include?(k) }
87
90
  end
88
91
 
89
92
  private
@@ -113,36 +116,5 @@ module Fusuma
113
116
  def expand_default_path(filename)
114
117
  File.expand_path "../../#{filename}", __FILE__
115
118
  end
116
-
117
- def cache(key)
118
- @cache ||= {}
119
- key = key.join(',') if key.is_a? Array
120
- if @cache.key?(key)
121
- @cache[key]
122
- else
123
- @cache[key] = block_given? ? yield : nil
124
- end
125
- end
126
- end
127
- end
128
-
129
- # activesupport-4.1.1/lib/active_support/core_ext/hash/keys.rb
130
- class Hash
131
- def deep_symbolize_keys
132
- deep_transform_keys do |key|
133
- begin
134
- key.to_sym
135
- rescue StandardError
136
- key
137
- end
138
- end
139
- end
140
-
141
- def deep_transform_keys(&block)
142
- result = {}
143
- each do |key, value|
144
- result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
145
- end
146
- result
147
119
  end
148
120
  end