fusuma 1.10.1 → 2.0.0
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.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +9 -0
- data/.rubocop.yml +27 -0
- data/.rubocop_todo.yml +34 -19
- data/.solargraph.yml +16 -0
- data/.travis.yml +1 -3
- data/CHANGELOG.md +38 -1
- data/CONTRIBUTING.md +72 -0
- data/Gemfile +17 -0
- data/README.md +53 -7
- data/fusuma.gemspec +4 -13
- data/lib/fusuma.rb +91 -29
- data/lib/fusuma/config.rb +59 -60
- data/lib/fusuma/config/index.rb +39 -6
- data/lib/fusuma/config/searcher.rb +166 -0
- data/lib/fusuma/config/yaml_duplication_checker.rb +42 -0
- data/lib/fusuma/custom_process.rb +13 -0
- data/lib/fusuma/device.rb +22 -7
- data/lib/fusuma/environment.rb +6 -4
- data/lib/fusuma/hash_support.rb +40 -0
- data/lib/fusuma/libinput_command.rb +17 -21
- data/lib/fusuma/multi_logger.rb +2 -6
- data/lib/fusuma/plugin/base.rb +18 -15
- data/lib/fusuma/plugin/buffers/buffer.rb +3 -2
- data/lib/fusuma/plugin/buffers/gesture_buffer.rb +34 -25
- data/lib/fusuma/plugin/buffers/timer_buffer.rb +46 -0
- data/lib/fusuma/plugin/detectors/detector.rb +26 -5
- data/lib/fusuma/plugin/detectors/pinch_detector.rb +109 -58
- data/lib/fusuma/plugin/detectors/rotate_detector.rb +91 -50
- data/lib/fusuma/plugin/detectors/swipe_detector.rb +93 -56
- data/lib/fusuma/plugin/events/event.rb +5 -4
- data/lib/fusuma/plugin/events/records/context_record.rb +27 -0
- data/lib/fusuma/plugin/events/records/gesture_record.rb +9 -6
- data/lib/fusuma/plugin/events/records/index_record.rb +46 -14
- data/lib/fusuma/plugin/events/records/record.rb +1 -1
- data/lib/fusuma/plugin/events/records/text_record.rb +2 -1
- data/lib/fusuma/plugin/executors/command_executor.rb +21 -6
- data/lib/fusuma/plugin/executors/executor.rb +45 -3
- data/lib/fusuma/plugin/filters/filter.rb +1 -1
- data/lib/fusuma/plugin/filters/libinput_device_filter.rb +6 -7
- data/lib/fusuma/plugin/filters/libinput_timeout_filter.rb +2 -2
- data/lib/fusuma/plugin/inputs/input.rb +64 -8
- data/lib/fusuma/plugin/inputs/libinput_command_input.rb +19 -9
- data/lib/fusuma/plugin/inputs/timer_input.rb +63 -0
- data/lib/fusuma/plugin/manager.rb +22 -29
- data/lib/fusuma/plugin/parsers/libinput_gesture_parser.rb +10 -8
- data/lib/fusuma/plugin/parsers/parser.rb +8 -9
- data/lib/fusuma/string_support.rb +16 -0
- data/lib/fusuma/version.rb +1 -1
- metadata +21 -150
data/lib/fusuma.rb
CHANGED
@@ -2,9 +2,10 @@
|
|
2
2
|
|
3
3
|
require_relative './fusuma/version'
|
4
4
|
require_relative './fusuma/multi_logger'
|
5
|
-
require_relative './fusuma/config
|
6
|
-
require_relative './fusuma/environment
|
7
|
-
require_relative './fusuma/
|
5
|
+
require_relative './fusuma/config'
|
6
|
+
require_relative './fusuma/environment'
|
7
|
+
require_relative './fusuma/device'
|
8
|
+
require_relative './fusuma/plugin/manager'
|
8
9
|
|
9
10
|
# this is top level module
|
10
11
|
module Fusuma
|
@@ -15,6 +16,8 @@ module Fusuma
|
|
15
16
|
set_trap
|
16
17
|
read_options(option)
|
17
18
|
instance = new
|
19
|
+
## NOTE: Uncomment following line to measure performance
|
20
|
+
# instance.run_with_lineprof
|
18
21
|
instance.run
|
19
22
|
end
|
20
23
|
|
@@ -47,8 +50,6 @@ module Fusuma
|
|
47
50
|
end
|
48
51
|
|
49
52
|
def load_custom_config(config_path = nil)
|
50
|
-
return unless config_path
|
51
|
-
|
52
53
|
Config.custom_path = config_path
|
53
54
|
end
|
54
55
|
end
|
@@ -63,59 +64,120 @@ module Fusuma
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def run
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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 }
|
75
88
|
end
|
89
|
+
LineProf.report(profile)
|
90
|
+
exit 0
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Plugin::Events::Event]
|
94
|
+
def input
|
95
|
+
Plugin::Inputs::Input.select(@inputs)
|
76
96
|
end
|
77
97
|
|
98
|
+
# @param [Plugin::Events::Event]
|
99
|
+
# @return [Plugin::Events::Event]
|
78
100
|
def filter(event)
|
79
101
|
event if @filters.any? { |f| f.filter(event) }
|
80
102
|
end
|
81
103
|
|
104
|
+
# @param [Plugin::Events::Event]
|
105
|
+
# @return [Plugin::Events::Event]
|
82
106
|
def parse(event)
|
83
107
|
@parsers.reduce(event) { |e, p| p.parse(e) if e }
|
84
108
|
end
|
85
109
|
|
110
|
+
# @param [Plugin::Events::Event]
|
111
|
+
# @return [Array<Plugin::Buffers::Buffer>]
|
112
|
+
# @return [NilClass]
|
86
113
|
def buffer(event)
|
87
|
-
@buffers.
|
114
|
+
@buffers.select { |b| b.buffer(event) }
|
88
115
|
end
|
89
116
|
|
90
117
|
# @param buffers [Array<Buffer>]
|
91
118
|
# @return [Array<Event>]
|
92
119
|
def detect(buffers)
|
93
|
-
@detectors.
|
94
|
-
|
95
|
-
|
96
|
-
|
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 }
|
97
127
|
end
|
128
|
+
|
129
|
+
return if events.empty?
|
130
|
+
|
131
|
+
events
|
98
132
|
end
|
99
133
|
|
100
|
-
# @param events [Array<Event>]
|
101
|
-
# @return [Event]
|
134
|
+
# @param events [Array<Plugin::Events::Event>]
|
135
|
+
# @return [Plugin::Events::Event] Event merged all records from arguments
|
102
136
|
# @return [NilClass] when event is NOT given
|
103
137
|
def merge(events)
|
104
|
-
|
105
|
-
|
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?
|
106
156
|
|
107
|
-
|
108
|
-
main_event
|
157
|
+
[condition, matched_context, event]
|
109
158
|
end
|
110
159
|
|
111
|
-
|
160
|
+
# @param event [Plugin::Events::Event]
|
161
|
+
def execute(condition, context, event)
|
112
162
|
return unless event
|
113
163
|
|
114
|
-
|
115
|
-
|
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
|
116
169
|
end
|
117
170
|
|
118
|
-
executor
|
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
|
119
181
|
end
|
120
182
|
|
121
183
|
def clear_expired_events
|
data/lib/fusuma/config.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative './multi_logger
|
4
|
-
require_relative './config/index
|
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'
|
5
8
|
require 'singleton'
|
6
9
|
require 'yaml'
|
7
10
|
|
@@ -9,11 +12,19 @@ require 'yaml'
|
|
9
12
|
module Fusuma
|
10
13
|
# read keymap from yaml file
|
11
14
|
class Config
|
15
|
+
class NotFoundError < StandardError; end
|
16
|
+
|
17
|
+
class InvalidFileError < StandardError; end
|
18
|
+
|
12
19
|
include Singleton
|
13
20
|
|
14
21
|
class << self
|
15
|
-
def search(
|
16
|
-
instance.search(
|
22
|
+
def search(index)
|
23
|
+
instance.search(index)
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_execute_key(index)
|
27
|
+
instance.find_execute_key(index)
|
17
28
|
end
|
18
29
|
|
19
30
|
def custom_path=(new_path)
|
@@ -21,14 +32,12 @@ module Fusuma
|
|
21
32
|
end
|
22
33
|
end
|
23
34
|
|
24
|
-
attr_reader :keymap
|
25
|
-
attr_reader :custom_path
|
35
|
+
attr_reader :keymap, :custom_path, :searcher
|
26
36
|
|
27
37
|
def initialize
|
38
|
+
@searcher = Searcher.new
|
28
39
|
@custom_path = nil
|
29
|
-
@cache = nil
|
30
40
|
@keymap = nil
|
31
|
-
reload
|
32
41
|
end
|
33
42
|
|
34
43
|
def custom_path=(new_path)
|
@@ -37,40 +46,61 @@ module Fusuma
|
|
37
46
|
end
|
38
47
|
|
39
48
|
def reload
|
40
|
-
@
|
41
|
-
|
42
|
-
MultiLogger.info "reload config
|
49
|
+
@searcher = Searcher.new
|
50
|
+
path = find_filepath
|
51
|
+
MultiLogger.info "reload config: #{path}"
|
52
|
+
@keymap = validate(path)
|
43
53
|
self
|
44
54
|
end
|
45
55
|
|
56
|
+
# @return [Hash] If check passes
|
57
|
+
# @raise [InvalidFileError] If check does not pass
|
58
|
+
def validate(path)
|
59
|
+
duplicates = []
|
60
|
+
YAMLDuplicationChecker.check(File.read(path), path) do |ignored, duplicate|
|
61
|
+
MultiLogger.error "#{path}: #{ignored.value} is duplicated"
|
62
|
+
duplicates << duplicate.value
|
63
|
+
end
|
64
|
+
raise InvalidFileError, "Detect duplicate keys #{duplicates}" unless duplicates.empty?
|
65
|
+
|
66
|
+
yamls = YAML.load_stream(File.read(path)).compact
|
67
|
+
yamls.map(&:deep_symbolize_keys)
|
68
|
+
rescue StandardError => e
|
69
|
+
MultiLogger.error e.message
|
70
|
+
raise InvalidFileError, e.message
|
71
|
+
end
|
72
|
+
|
46
73
|
# @param index [Index]
|
74
|
+
# @param context [Hash]
|
47
75
|
def search(index)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
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) }
|
63
90
|
end
|
64
91
|
|
65
92
|
private
|
66
93
|
|
67
|
-
def
|
94
|
+
def find_filepath
|
68
95
|
filename = 'fusuma/config.yml'
|
69
|
-
if custom_path
|
70
|
-
expand_custom_path
|
96
|
+
if custom_path
|
97
|
+
return expand_custom_path if File.exist?(expand_custom_path)
|
98
|
+
|
99
|
+
raise NotFoundError, "#{expand_custom_path} is NOT FOUND"
|
71
100
|
elsif File.exist?(expand_config_path(filename))
|
72
101
|
expand_config_path(filename)
|
73
102
|
else
|
103
|
+
MultiLogger.warn "config file: #{expand_config_path(filename)} is NOT FOUND"
|
74
104
|
expand_default_path(filename)
|
75
105
|
end
|
76
106
|
end
|
@@ -86,36 +116,5 @@ module Fusuma
|
|
86
116
|
def expand_default_path(filename)
|
87
117
|
File.expand_path "../../#{filename}", __FILE__
|
88
118
|
end
|
89
|
-
|
90
|
-
def cache(key)
|
91
|
-
@cache ||= {}
|
92
|
-
key = key.join(',') if key.is_a? Array
|
93
|
-
if @cache.key?(key)
|
94
|
-
@cache[key]
|
95
|
-
else
|
96
|
-
@cache[key] = block_given? ? yield : nil
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# activesupport-4.1.1/lib/active_support/core_ext/hash/keys.rb
|
103
|
-
class Hash
|
104
|
-
def deep_symbolize_keys
|
105
|
-
deep_transform_keys do |key|
|
106
|
-
begin
|
107
|
-
key.to_sym
|
108
|
-
rescue StandardError
|
109
|
-
key
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def deep_transform_keys(&block)
|
115
|
-
result = {}
|
116
|
-
each do |key, value|
|
117
|
-
result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
|
118
|
-
end
|
119
|
-
result
|
120
119
|
end
|
121
120
|
end
|
data/lib/fusuma/config/index.rb
CHANGED
@@ -19,6 +19,11 @@ module Fusuma
|
|
19
19
|
[Key.new(keys)]
|
20
20
|
end
|
21
21
|
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
@keys.map(&:inspect)
|
25
|
+
end
|
26
|
+
|
22
27
|
attr_reader :keys
|
23
28
|
|
24
29
|
def cache_key
|
@@ -32,17 +37,45 @@ module Fusuma
|
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
40
|
+
# @return [Index]
|
41
|
+
def with_context
|
42
|
+
keys = @keys.map do |key|
|
43
|
+
next if Searcher.skip? && key.skippable
|
44
|
+
|
45
|
+
if Searcher.fallback? && key.fallback
|
46
|
+
key.fallback
|
47
|
+
else
|
48
|
+
key
|
49
|
+
end
|
50
|
+
end
|
51
|
+
self.class.new(keys.compact)
|
52
|
+
end
|
53
|
+
|
35
54
|
# Keys in Index
|
36
55
|
class Key
|
37
|
-
def initialize(symbol_word, skippable: false)
|
56
|
+
def initialize(symbol_word, skippable: false, fallback: nil)
|
38
57
|
@symbol = begin
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
58
|
+
symbol_word.to_sym
|
59
|
+
rescue StandardError
|
60
|
+
symbol_word
|
61
|
+
end
|
62
|
+
|
43
63
|
@skippable = skippable
|
64
|
+
|
65
|
+
@fallback = begin
|
66
|
+
fallback.to_sym
|
67
|
+
rescue StandardError
|
68
|
+
fallback
|
69
|
+
end
|
44
70
|
end
|
45
|
-
|
71
|
+
|
72
|
+
def inspect
|
73
|
+
skip_marker = @skippable && Searcher.skip? ? '(skip)' : ''
|
74
|
+
fallback_marker = @fallback && Searcher.fallback? ? '(fallback)' : ''
|
75
|
+
"#{@symbol}#{skip_marker}#{fallback_marker}"
|
76
|
+
end
|
77
|
+
|
78
|
+
attr_reader :symbol, :skippable, :fallback
|
46
79
|
end
|
47
80
|
end
|
48
81
|
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Index for searching value from config.yml
|
4
|
+
module Fusuma
|
5
|
+
class Config
|
6
|
+
# Search config.yml
|
7
|
+
class Searcher
|
8
|
+
def initialize
|
9
|
+
@cache = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param index [Index]
|
13
|
+
# @param location [Hash]
|
14
|
+
# @return [NilClass]
|
15
|
+
# @return [Hash]
|
16
|
+
# @return [Object]
|
17
|
+
def search(index, location:)
|
18
|
+
key = index.keys.first
|
19
|
+
return location if key.nil?
|
20
|
+
|
21
|
+
return nil if location.nil?
|
22
|
+
|
23
|
+
return nil unless location.is_a?(Hash)
|
24
|
+
|
25
|
+
next_index = Index.new(index.keys[1..-1])
|
26
|
+
|
27
|
+
value = nil
|
28
|
+
next_location_cadidates(location, key).find do |next_location|
|
29
|
+
value = search(next_index, location: next_location)
|
30
|
+
end
|
31
|
+
value
|
32
|
+
end
|
33
|
+
|
34
|
+
def search_with_context(index, location:, context:)
|
35
|
+
return nil if location.nil?
|
36
|
+
|
37
|
+
return search(index, location: location[0]) if context == {}
|
38
|
+
|
39
|
+
new_location = location.find do |conf|
|
40
|
+
search(index, location: conf) if conf[:context] == context
|
41
|
+
end
|
42
|
+
search(index, location: new_location)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param index [Index]
|
46
|
+
# @param location [Hash]
|
47
|
+
# @return [NilClass]
|
48
|
+
# @return [Hash]
|
49
|
+
# @return [Object]
|
50
|
+
def search_with_cache(index, location:)
|
51
|
+
cache([index.cache_key, Searcher.context, Searcher.skip?, Searcher.fallback?]) do
|
52
|
+
search_with_context(index, location: location, context: Searcher.context)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def cache(key)
|
57
|
+
@cache ||= {}
|
58
|
+
key = key.join(',') if key.is_a? Array
|
59
|
+
if @cache.key?(key)
|
60
|
+
@cache[key]
|
61
|
+
else
|
62
|
+
@cache[key] = block_given? ? yield : nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# next locations' candidates sorted by priority
|
69
|
+
# 1. look up location with key
|
70
|
+
# 2. fallback to other key
|
71
|
+
# 3. skip the key and go to child location
|
72
|
+
def next_location_cadidates(location, key)
|
73
|
+
[
|
74
|
+
location[key.symbol],
|
75
|
+
Searcher.skip? && key.skippable && location
|
76
|
+
].compact
|
77
|
+
end
|
78
|
+
|
79
|
+
class << self
|
80
|
+
# @return [Hash]
|
81
|
+
def conditions(&block)
|
82
|
+
{
|
83
|
+
nothing: -> { block.call },
|
84
|
+
skip: -> { Config::Searcher.skip { block.call } }
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Execute block with specified conditions
|
89
|
+
# @param conidtion [Symbol]
|
90
|
+
# @return [Object]
|
91
|
+
def with_condition(condition, &block)
|
92
|
+
conditions(&block)[condition].call
|
93
|
+
end
|
94
|
+
|
95
|
+
# Execute block with all conditions
|
96
|
+
# @return [Array<Symbol, Object>]
|
97
|
+
def find_condition(&block)
|
98
|
+
conditions(&block).find do |c, l|
|
99
|
+
result = l.call
|
100
|
+
return [c, result] if result
|
101
|
+
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Search with context from load_streamed Config
|
107
|
+
# @param context [Hash]
|
108
|
+
# @return [Object]
|
109
|
+
def with_context(context, &block)
|
110
|
+
@context = context || {}
|
111
|
+
result = block.call
|
112
|
+
@context = {}
|
113
|
+
result
|
114
|
+
end
|
115
|
+
|
116
|
+
# Return a matching context from config
|
117
|
+
# @params request_context [Hash]
|
118
|
+
# @return [Hash]
|
119
|
+
def find_context(request_context, &block)
|
120
|
+
# Search in blocks in the following order.
|
121
|
+
# 1. complete match config[:context] == request_context
|
122
|
+
# 2. partial match config[:context] =~ request_context
|
123
|
+
# 3. no context
|
124
|
+
Config.instance.keymap.each do |config|
|
125
|
+
next unless config[:context] == request_context
|
126
|
+
return config[:context] if with_context(config[:context]) { block.call }
|
127
|
+
end
|
128
|
+
if request_context.keys.size > 1
|
129
|
+
Config.instance.keymap.each do |config|
|
130
|
+
next if config[:context].nil?
|
131
|
+
|
132
|
+
next unless config[:context].all? { |k, v| request_context[k] == v }
|
133
|
+
return config[:context] if with_context(config[:context]) { block.call }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
return {} if with_context({}) { block.call }
|
137
|
+
end
|
138
|
+
|
139
|
+
attr_reader :context
|
140
|
+
|
141
|
+
def fallback?
|
142
|
+
@fallback
|
143
|
+
end
|
144
|
+
|
145
|
+
def skip?
|
146
|
+
@skip
|
147
|
+
end
|
148
|
+
|
149
|
+
# switch context for fallback
|
150
|
+
def fallback(&block)
|
151
|
+
@fallback = true
|
152
|
+
result = block.call
|
153
|
+
@fallback = false
|
154
|
+
result
|
155
|
+
end
|
156
|
+
|
157
|
+
def skip(&block)
|
158
|
+
@skip = true
|
159
|
+
result = block.call
|
160
|
+
@skip = false
|
161
|
+
result
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|