pleiades 0.1.1 → 0.1.2

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -2
  3. data/README.md +23 -0
  4. data/Rakefile +6 -0
  5. data/lib/pleiades/core.rb +1 -0
  6. data/lib/pleiades/core/client.rb +52 -0
  7. data/lib/pleiades/core/client/wrapper.rb +55 -0
  8. data/lib/pleiades/core/command.rb +11 -7
  9. data/lib/pleiades/core/command/base_command.rb +1 -16
  10. data/lib/pleiades/core/command/executor.rb +16 -0
  11. data/lib/pleiades/core/command/factory.rb +50 -0
  12. data/lib/pleiades/core/command/router.rb +28 -27
  13. data/lib/pleiades/core/command/routing/event_judge_methods.rb +51 -0
  14. data/lib/pleiades/core/command/routing/event_proccessor.rb +122 -0
  15. data/lib/pleiades/core/command/routing/nest_blocks.rb +238 -0
  16. data/lib/pleiades/core/command/routing/path_builder.rb +18 -0
  17. data/lib/pleiades/core/command/routing/reflection.rb +13 -0
  18. data/lib/pleiades/core/command/routing/result.rb +47 -0
  19. data/lib/pleiades/core/command/routing/route_refine.rb +49 -0
  20. data/lib/pleiades/core/command/routing/validator.rb +72 -0
  21. data/lib/pleiades/core/command/routing_proxy.rb +126 -0
  22. data/lib/pleiades/core/config.rb +24 -17
  23. data/lib/pleiades/core/constants.rb +10 -7
  24. data/lib/pleiades/core/util.rb +9 -7
  25. data/lib/pleiades/core_ext/line/bot/event/base.rb +16 -7
  26. data/lib/pleiades/core_ext/line/bot/event/postback.rb +6 -4
  27. data/lib/pleiades/generators/pleiades/command/command_generator.rb +58 -51
  28. data/lib/pleiades/generators/pleiades/install/install_generator.rb +11 -12
  29. data/lib/pleiades/generators/pleiades/install/templates/config.yml +11 -1
  30. data/lib/pleiades/generators/pleiades/install/templates/pleiades.rb +1 -0
  31. data/lib/pleiades/generators/pleiades/install/templates/routing_proxy.rb +13 -0
  32. data/lib/pleiades/generators/pleiades/setup/setup_generator.rb +100 -98
  33. data/lib/pleiades/generators/pleiades/setup/templates/api_controller.rb +4 -15
  34. data/lib/pleiades/generators/pleiades/setup/templates/base_command.rb +2 -6
  35. data/lib/pleiades/generators/pleiades/setup/templates/command_common.erb +2 -6
  36. data/lib/pleiades/railtie.rb +3 -11
  37. data/lib/pleiades/version.rb +1 -1
  38. data/pleiades.gemspec +1 -0
  39. metadata +32 -8
  40. data/lib/pleiades/core/.keep +0 -0
  41. data/lib/pleiades/core/command/event_proccessor.rb +0 -87
  42. data/lib/pleiades/core/command/nest_blocks.rb +0 -55
  43. data/lib/pleiades/core_ext/.keep +0 -0
@@ -0,0 +1,238 @@
1
+ require 'active_support/core_ext'
2
+ require 'pleiades/core/command/routing/route_refine'
3
+
4
+ module Pleiades
5
+ module Command
6
+ module Routing
7
+ # ブロック付きメソッド郡。
8
+ #
9
+ # イベントメソッドに渡すキーワード引数を、ネストしたリソースとしてまとめて実行する。
10
+ #
11
+ module NestBlocks
12
+ extend ActiveSupport::Concern
13
+ include Pleiades::Command::Routing::RouteRefine
14
+
15
+ # スコープをネストする。
16
+ #
17
+ # ## メソッド呼び出し時のメソッド名による処理の違い
18
+ # scope, scope_append
19
+ # 引数を後方追加する
20
+ #
21
+ # scope_unshift
22
+ # 引数を前方追加する
23
+ #
24
+ # ## EXAMPLE(単一リソースのネスト)
25
+ # ### 使用しない
26
+ # postback scope :hoge, action: :fuga
27
+ # postback scope :hoge, action: :piyo
28
+ #
29
+ # ### 使用する
30
+ # scope :hoge do
31
+ # postback action: :fuga
32
+ # postback action: :piyo
33
+ # end
34
+ #
35
+ # ## EXAMPLE(複数リソースのネスト)
36
+ # ### 使用しない
37
+ # postback scope :hoge, action: :piyo
38
+ # postback scope :fuga, action: :piyo
39
+ #
40
+ # ### 使用する
41
+ # scope :hoge, :fuga do
42
+ # postback action: :piyo
43
+ # end
44
+ #
45
+ def scope(*scopes, &block)
46
+ scopes.flatten.map { |scp| nest_block __callee__, scp, &block }
47
+ end
48
+ alias :scope_append scope
49
+ alias :scope_unshift scope
50
+
51
+ # アクションをネストする。
52
+ #
53
+ # EXAMPLE
54
+ # ## 使用しない
55
+ # postback scope :hoge, action: :piyo
56
+ # postback scope :fuga, action: :piyo
57
+ #
58
+ # ## 使用する
59
+ # action :piyo do
60
+ # postback scope :hoge
61
+ # postback scope :fuga
62
+ # end
63
+ #
64
+ # ## EXAMPLE(単一リソースのネスト)
65
+ # ### 使用しない
66
+ # postback scope :hoge, action: :piyo
67
+ # postback scope :fuga, action: :piyo
68
+ #
69
+ # ### 使用する
70
+ # action :piyo do
71
+ # postback scope :hoge
72
+ # postback scope :fuga
73
+ # end
74
+ #
75
+ # ## EXAMPLE(複数リソースのネスト)
76
+ # ### 使用しない
77
+ # postback scope :piyo, action: :hoge
78
+ # postback scope :piyo, action: :fuga
79
+ #
80
+ # ### 使用する
81
+ # action :hoge, :fuga do
82
+ # postback scope: :piyo
83
+ # end
84
+ #
85
+ def action(*actions, &block)
86
+ actions.flatten.map { |act| nest_block __method__, act, &block }
87
+ end
88
+
89
+ # Concernsをネストしたリソースに適用する。
90
+ #
91
+ # concern HogeModule do
92
+ # postback # => HogeModule が include される
93
+ # concern FugaModule do
94
+ # postback # => HogeModule, FugaModule が include される
95
+ # end
96
+ # end
97
+ #
98
+ def concern(*concerns, &block)
99
+ nest_block __method__, concerns, &block
100
+ end
101
+
102
+ # コマンドクラスへの呼び出すメソッド名をネストしたリソースに適用する。
103
+ #
104
+ # ## EXAMPLE
105
+ # ### 使用しない
106
+ # postback scope :hoge, action: :fuga, method: :greet
107
+ # # => Hoge::Fuga#greet が実行される。
108
+ #
109
+ # postback scope :hoge, action: :piyo, method: :greet
110
+ # # => Hoge::Piyo#greet が実行される
111
+ #
112
+ # ### 使用する
113
+ # method: :greet do
114
+ # postback scope :hoge, action: :fuga
115
+ # postback scope :hoge, action: :piyo
116
+ # end
117
+ #
118
+ alias :_method_ method
119
+ def method(method_name, &block)
120
+ if block_given?
121
+ nest_block __method__, method_name, &block
122
+ else
123
+ _method_(method_name)
124
+ end
125
+ end
126
+
127
+ # 複数のネストメソッドを呼び出す。
128
+ #
129
+ # EXAMPLE
130
+ # ## 使用しない
131
+ #
132
+ # scope :hoge do
133
+ # action :fuga do
134
+ # postback
135
+ # end
136
+ # end
137
+ #
138
+ # ## 使用する
139
+ # nest_blocks scope: :hoge, action: :fuga do
140
+ # postback
141
+ # end
142
+ #
143
+ # @param [Hash] key: ネストメソッド名, val: 引数
144
+ #
145
+ def nest_blocks(**keywords, &block)
146
+ validate_event_keywords(keywords.deep_dup.merge(type: ''))
147
+
148
+ nested_options = make_nested_options(keywords)
149
+
150
+ return if nested_options == false
151
+
152
+ nested_options.each do |opt|
153
+ nest(@options.deep_dup.merge(opt), &block)
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ def make_nested_options(keywords)
160
+ nested_options =
161
+ keywords.each_pair
162
+ .each_with_object([[self]]) do |(method_name, arg), arr|
163
+ routers = arr.last
164
+ break if routers.include?(false)
165
+
166
+ arr << nest_routers(routers) { method(method_name).call(arg) }.flatten
167
+ end.last
168
+
169
+ nested_options ? nested_options.map(&:options) : false
170
+ end
171
+
172
+ def nest_routers(routers, &proc)
173
+ routers.map do |router|
174
+ nest_router = Array.wrap(nest(router.options.deep_dup, &proc))
175
+
176
+ break if nest_router.include?(false)
177
+
178
+ nest_router
179
+ end || [false]
180
+ end
181
+
182
+ def callable_nest_methods
183
+ reject_methods = %i[nest_blocks _method_]
184
+
185
+ Pleiades::Command::Routing::NestBlocks.public_instance_methods.without(*reject_methods)
186
+ end
187
+
188
+ def default_options
189
+ {
190
+ scope: [],
191
+ action: '',
192
+ method: '',
193
+ concern: []
194
+ }.merge(Pleiades::Config.router_default_option)
195
+ end
196
+
197
+ def nest_block(method_name, context, &block)
198
+ @_options = __send__ "merge_#{method_name}", context
199
+ @_self = Pleiades::Command::Router.new(@_options)
200
+
201
+ block_given? ? @_self.instance_eval(&block) : @_self
202
+ end
203
+
204
+ def merge_scope(context)
205
+ operator =
206
+ case __callee__
207
+ when /^merge_scope(_append)?$/
208
+ :append
209
+ when /^merge_scope_unshift$/
210
+ :unshift
211
+ end
212
+
213
+ __merge__ ->(option) { option[:scope].__send__ operator, context }
214
+ end
215
+ alias :merge_scope_append merge_scope
216
+ alias :merge_scope_unshift merge_scope
217
+
218
+ def merge_action(action)
219
+ __merge__ ->(option) { option[:action] = action }
220
+ end
221
+
222
+ def merge_concern(concerns)
223
+ __merge__ ->(option) { option[:concern].concat(concerns).flatten }
224
+ end
225
+
226
+ def merge_method(method)
227
+ __merge__ ->(option) { option[:method] = method }
228
+ end
229
+
230
+ def __merge__(proc)
231
+ dup_options = @options.deep_dup
232
+ proc.call(dup_options)
233
+ dup_options
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,18 @@
1
+ module Pleiades
2
+ module Command
3
+ module Routing
4
+ module PathBuilder
5
+ extend ActiveSupport::Concern
6
+ def normalize_path(scope = nil, action = nil)
7
+ dirs = []
8
+
9
+ dirs << @options[:scope] if @options[:scope].any?
10
+ dirs << scope if scope
11
+ dirs << (action || @options[:action])
12
+
13
+ dirs.join('/')
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module Pleiades
2
+ module Command
3
+ module Routing
4
+ module Reflection
5
+ extend ActiveSupport::Concern
6
+
7
+ def __event_name__
8
+ @event.type
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ module Pleiades
2
+ module Command
3
+ module Routing
4
+ class Result
5
+ include Pleiades::Command::Routing::PathBuilder
6
+
7
+ def self.create(options, event_args = {})
8
+ new(options, event_args).send :create
9
+ end
10
+
11
+ private
12
+
13
+ def initialize(options, event_args)
14
+ @options = options
15
+ @event_args = event_args
16
+ end
17
+
18
+ def create
19
+ attributes.each_with_object({}) do |method_name, result|
20
+ result.store(method_name, send(method_name))
21
+ end
22
+ end
23
+
24
+ def attributes
25
+ rejects = %i[initialize create] << __method__
26
+ private_methods(false).without(*rejects)
27
+ end
28
+
29
+ def command_path
30
+ normalize_path(@event_args[:scope], @event_args[:action])
31
+ end
32
+
33
+ def call_method
34
+ @options[:method]
35
+ end
36
+
37
+ def concern
38
+ @options[:concern]
39
+ end
40
+
41
+ def executor
42
+ @options[:executor]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ module Pleiades
2
+ module Command
3
+ module Routing
4
+ module RouteRefine
5
+ # 指定したイベントの時のみブロックを実行する
6
+ #
7
+ # ## EXAMPLE
8
+ # only_events :postback do
9
+ # event scope :hoge, action: :fuga
10
+ # # => Hoge::Fuga を postbackイベントとして実行する
11
+ # end
12
+ #
13
+ def only_events(*events, &block)
14
+ return false unless callable_event_type?(events)
15
+
16
+ return self unless block_given?
17
+
18
+ instance_eval(&block)
19
+ end
20
+
21
+ # 指定したトークタイプの時のみブロックを実行する
22
+ #
23
+ # ## EXAMPLE
24
+ # talk_type :user do
25
+ # p @event.source.type # => "user"
26
+ # postback scope :hoge, action: :fuga
27
+ # end
28
+ #
29
+ def talk_type(*talk_types, &block)
30
+ return false unless callable_talk_type?(talk_types)
31
+
32
+ return self unless block_given?
33
+
34
+ instance_eval(&block)
35
+ end
36
+
37
+ private
38
+
39
+ def callable_talk_type?(types)
40
+ types.map(&:to_s).include?(@event.source.type)
41
+ end
42
+
43
+ def callable_event_type?(types)
44
+ types.map(&:to_s).include?(__event_name__)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,72 @@
1
+ module Pleiades
2
+ module Command
3
+ module Routing
4
+ module Validator
5
+ extend ActiveSupport::Concern
6
+
7
+ private
8
+
9
+ # `match` の引数を検証する
10
+ #
11
+ # @param [Hash] match_keywords
12
+ #
13
+ # @raise 無効なイベント名が存在する場合に送出される
14
+ #
15
+ # @return [true] 入力値が妥当な場合
16
+ #
17
+ def validate_match_keywords(match_keywords)
18
+ match_keywords.fetch(:via) do |_|
19
+ raise ArgumentError, "Make sure to specify keyword `via:' in the `match' method."
20
+ end
21
+
22
+ match_keywords[:via].each_key do |event_name|
23
+ no_event = -> { raise "Unexpected event method `#{event_name}' specified in keyword `via:'." }
24
+ event_types.find(no_event) { |event| event_name.to_s == event }
25
+ end
26
+
27
+ true
28
+ end
29
+
30
+ # イベントメソッドの引数を検証する
31
+ #
32
+ # @param [Hash] keywords
33
+ #
34
+ # @raise [ArgumentError] 無効なキーワード名が存在する場合
35
+ #
36
+ # @return [True] 入力値が妥当な場合
37
+ #
38
+ def validate_event_keywords(keywords)
39
+ @_keywords = keywords.deep_dup
40
+ event_name = @_keywords.delete(:type)
41
+
42
+ event_specify_keywords = per_event_specify_keywords(event_name)
43
+
44
+ @_keywords.keys.each do |key|
45
+ next if event_specify_keywords.include?(key)
46
+
47
+ raise ArgumentError, <<~MES
48
+ #{__FILE__}:#{__LINE__}:in `#{__method__}'
49
+ #{event_name} event, key `#{key}' cannot be specified.
50
+ MES
51
+ end
52
+ true
53
+ end
54
+
55
+ def per_event_specify_keywords(event_name)
56
+ event_name = event_name.to_sym
57
+
58
+ commons = callable_nest_methods
59
+ per_events =
60
+ {
61
+ text: %i[pattern],
62
+ sticker: %i[package_id sticker_id]
63
+ }
64
+
65
+ extract_keywords = per_events[event_name]
66
+
67
+ extract_keywords ? commons.concat(extract_keywords) : commons
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,126 @@
1
+ require 'pleiades/core/command/routing/route_refine'
2
+
3
+ module Pleiades
4
+ module Command
5
+ class RoutingProxy
6
+ include Pleiades::Command::Routing::RouteRefine
7
+
8
+ class << self
9
+ def routing(&block)
10
+ @_ = new(@event)
11
+
12
+ block_given? ? @_.instance_eval(&block) : @_.default_routing
13
+ end
14
+
15
+ # 読み込む router_files を決める。
16
+ #
17
+ # @param [Line::Bot::Event] Eventクラス
18
+ #
19
+ # @return [Array<Pathname, ...>] 読み込むファイル一覧
20
+ #
21
+ def collect_router_file(event)
22
+ @event = event
23
+ proxy_exists? ? load(proxy_file) : routing
24
+
25
+ collect!
26
+ end
27
+
28
+ private
29
+
30
+ def proxy_exists?
31
+ FileTest.exist?(proxy_file)
32
+ end
33
+
34
+ def proxy_file
35
+ Pleiades::Constants::File::ROUTING_PROXY
36
+ end
37
+
38
+ def collect!
39
+ @_.routers
40
+ end
41
+ end
42
+
43
+ attr_reader :routers, :mount_pairs
44
+
45
+ def initialize(event)
46
+ @event = event
47
+ @routers = []
48
+ @mount_pairs = {}
49
+
50
+ mount default: Pleiades::Constants::File::CONFIG_DIR_PATH
51
+ end
52
+
53
+ def default_routing
54
+ add default_router
55
+ end
56
+
57
+ def add(router_name, mnt_key: :default)
58
+ no_keyword_err = lambda { |key|
59
+ <<~MES
60
+ from #{__FILE__}:#{__LINE__ - 1}:in `#{__method__}`:
61
+ Unmounted key `#{key}`. Please call `mount #{key}: 'path/to/router'` before `#{__method__}`.
62
+ MES
63
+ }
64
+ dir = @mount_pairs.fetch(mnt_key) { |key| raise no_keyword_err.call(key) }
65
+
66
+ @routers << "#{dir}/#{router_name}.rb"
67
+ end
68
+
69
+ # `add`で指定するキーワードを登録する。
70
+ #
71
+ # @param [symbol_name: path, ...]
72
+ # symbol_name : キーワード
73
+ # path : プロジェクトホームからの相対パスまたは、絶対パス
74
+ #
75
+ # ## EXAMPLE
76
+ # mount hoge: 'path/to/hoge'
77
+ # add :fuga, mnt: :hoge
78
+ # # => path/to/hoge/fuga.rb
79
+ #
80
+ def mount(**paths)
81
+ validate_mount_keys(paths.keys)
82
+
83
+ paths.each_pair do |symbol, path|
84
+ @mount_pairs.store(symbol, path =~ %r{(\S+)/$} ? $1 : path)
85
+ end
86
+ end
87
+
88
+ def unmount(*mnt_symbols)
89
+ return @mount_pairs.clear && nil if mnt_symbols.include?(:all)
90
+
91
+ warn = ->(key) { "\nWarning from #{__FILE__}:#{__LINE__ - 3}:in `#{__method__}`: `#{key}` is unmounted." }
92
+
93
+ mnt_symbols.each do |symbol|
94
+ @mount_pairs.delete(symbol) { |sym| puts warn.call(sym) }
95
+ end
96
+ end
97
+
98
+ def unmount_all
99
+ unmount :all
100
+ end
101
+
102
+ def default_router
103
+ :router
104
+ end
105
+
106
+ private
107
+
108
+ def validate_mount_keys(keys)
109
+ keys.each do |key|
110
+ raise "`#{key}` is reserved words. Please specify another key name." if mount_reserved_word?(key)
111
+
112
+ raise "`#{key}` is already mounted." if mounted?(key)
113
+ end
114
+ true
115
+ end
116
+
117
+ def mount_reserved_word?(word)
118
+ %i[all].include?(word.to_sym)
119
+ end
120
+
121
+ def mounted?(key)
122
+ @mount_pairs.key?(key)
123
+ end
124
+ end
125
+ end
126
+ end