tengine_core 0.5.28

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 (156) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +40 -0
  4. data/Gemfile.lock +95 -0
  5. data/README.md +54 -0
  6. data/Rakefile +44 -0
  7. data/VERSION +1 -0
  8. data/bin/tengine_atd +8 -0
  9. data/bin/tengine_heartbeat_watchd +8 -0
  10. data/bin/tengined +182 -0
  11. data/examples/VERSION +1 -0
  12. data/examples/uc01_execute_processing_for_event.rb +11 -0
  13. data/examples/uc02_fire_another_event.rb +16 -0
  14. data/examples/uc03_2handlers_for_1event.rb +16 -0
  15. data/examples/uc08_if_both_a_and_b_occurs.rb +11 -0
  16. data/examples/uc10_if_the_event_occurs_at_the_server.rb +15 -0
  17. data/examples/uc50_commit_event_at_first.rb +17 -0
  18. data/examples/uc51_commit_event_at_first_submit.rb +29 -0
  19. data/examples/uc52_commit_event_after_all_handler_submit.rb +31 -0
  20. data/examples/uc52_never_commit_event_unless_all_handler_submit.rb +31 -0
  21. data/examples/uc60_event_in_handler.rb +18 -0
  22. data/examples/uc62_session_in_driver.rb +16 -0
  23. data/examples/uc64_safety_countup.rb +14 -0
  24. data/examples/uc70_driver_enabled_on_activation.rb +13 -0
  25. data/examples/uc71_driver_disabled_on_activation.rb +14 -0
  26. data/examples/uc72_setup_eventmachine.rb +17 -0
  27. data/examples/uc80_raise_io_error.rb +10 -0
  28. data/examples/uc81_raise_runtime_error.rb +10 -0
  29. data/examples2/driver01.rb +18 -0
  30. data/examples2/driver02.rb +19 -0
  31. data/examples2/uc08_if_both_a_and_b_occurs.rb +13 -0
  32. data/examples2/uc10_if_the_event_occurs_at_the_server.rb +18 -0
  33. data/examples2/uc51_commit_event_at_first_submit_1.rb +16 -0
  34. data/examples2/uc51_commit_event_at_first_submit_2.rb +17 -0
  35. data/examples2/uc51_commit_event_at_first_submit_3.rb +17 -0
  36. data/examples2/uc62_session_in_driver.rb +16 -0
  37. data/examples2/uc71_driver_disabled_on_activation.rb +16 -0
  38. data/failure_examples/VERSION +1 -0
  39. data/failure_examples/uc53_submit_outside_of_handler.rb +15 -0
  40. data/failure_examples/uc61_event_outside_of_handler.rb +12 -0
  41. data/failure_examples/uc63_session_outside_of_driver.rb +13 -0
  42. data/lib/tengine/core.rb +74 -0
  43. data/lib/tengine/core/bootstrap.rb +123 -0
  44. data/lib/tengine/core/collection_accessible.rb +34 -0
  45. data/lib/tengine/core/config.rb +10 -0
  46. data/lib/tengine/core/config/atd.rb +225 -0
  47. data/lib/tengine/core/config/core.rb +319 -0
  48. data/lib/tengine/core/config/heartbeat_watcher.rb +229 -0
  49. data/lib/tengine/core/connection_test/.gitignore +1 -0
  50. data/lib/tengine/core/connection_test/fire_bar_on_foo.rb +16 -0
  51. data/lib/tengine/core/driveable.rb +213 -0
  52. data/lib/tengine/core/driver.rb +69 -0
  53. data/lib/tengine/core/driver/finder.rb +42 -0
  54. data/lib/tengine/core/dsl_evaluator.rb +110 -0
  55. data/lib/tengine/core/dsl_filter_def.rb +11 -0
  56. data/lib/tengine/core/dsl_loader.rb +108 -0
  57. data/lib/tengine/core/event.rb +145 -0
  58. data/lib/tengine/core/event/finder.rb +82 -0
  59. data/lib/tengine/core/event_exception_reportable.rb +88 -0
  60. data/lib/tengine/core/event_wrapper.rb +21 -0
  61. data/lib/tengine/core/find_by_name.rb +31 -0
  62. data/lib/tengine/core/handler.rb +152 -0
  63. data/lib/tengine/core/handler_path.rb +33 -0
  64. data/lib/tengine/core/heartbeat_watcher.rb +161 -0
  65. data/lib/tengine/core/io_to_logger.rb +22 -0
  66. data/lib/tengine/core/kernel.rb +510 -0
  67. data/lib/tengine/core/kernel_runtime.rb +91 -0
  68. data/lib/tengine/core/method_traceable.rb +38 -0
  69. data/lib/tengine/core/mongoid_fix.rb +19 -0
  70. data/lib/tengine/core/mutex.rb +177 -0
  71. data/lib/tengine/core/optimistic_lock.rb +69 -0
  72. data/lib/tengine/core/plugins.rb +54 -0
  73. data/lib/tengine/core/schedule.rb +21 -0
  74. data/lib/tengine/core/scheduler.rb +156 -0
  75. data/lib/tengine/core/selectable_attr.rb +29 -0
  76. data/lib/tengine/core/session.rb +21 -0
  77. data/lib/tengine/core/session_wrapper.rb +68 -0
  78. data/lib/tengine/core/setting.rb +21 -0
  79. data/lib/tengine/core/validation.rb +36 -0
  80. data/lib/tengine/errors.rb +18 -0
  81. data/lib/tengine/rspec.rb +8 -0
  82. data/lib/tengine/rspec/context_wrapper.rb +51 -0
  83. data/lib/tengine/rspec/extension.rb +53 -0
  84. data/lib/tengine_core.rb +23 -0
  85. data/spec/factories/tengine_core_drivers.rb +10 -0
  86. data/spec/factories/tengine_core_events.rb +14 -0
  87. data/spec/factories/tengine_core_handler_paths.rb +9 -0
  88. data/spec/factories/tengine_core_handlers.rb +9 -0
  89. data/spec/factories/tengine_core_sessions.rb +9 -0
  90. data/spec/mongoid.yml +35 -0
  91. data/spec/spec_helper.rb +48 -0
  92. data/spec/support/mongo_index_key_log.rb +91 -0
  93. data/spec/tengine/core/bootstrap_spec.rb +278 -0
  94. data/spec/tengine/core/bugfix/bind_dsl_file_in_multi_byte_dir_spec.rb +21 -0
  95. data/spec/tengine/core/bugfix/enabled_on_activation_spec.rb +112 -0
  96. data/spec/tengine/core/bugfix/receive_event_spec.rb +133 -0
  97. data/spec/tengine/core/bugfix/use_dsl_version_method.rb +12 -0
  98. data/spec/tengine/core/bugfix/use_dsl_version_method_spec.rb +28 -0
  99. data/spec/tengine/core/bugfix/use_event_in_handler_dsl.rb +11 -0
  100. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/206/343/202/231/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215/source_location_encoding.rb +35 -0
  101. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/206/343/202/231/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215//351/235/236ASCII/343/201/256/343/203/225/343/202/241/343/202/244/343/203/253/345/220/215_dsl.rb +38 -0
  102. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/207/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215/source_location_encoding.rb +35 -0
  103. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/207/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215//351/235/236ASCII/343/201/256/343/203/225/343/202/241/343/202/244/343/203/253/345/220/215_dsl.rb +38 -0
  104. data/spec/tengine/core/config/atd_spec.rb +62 -0
  105. data/spec/tengine/core/config/core_spec.rb +479 -0
  106. data/spec/tengine/core/config/heartbeat_watcher_spec.rb +62 -0
  107. data/spec/tengine/core/config/syntax_error_in_erb.yml.erb +13 -0
  108. data/spec/tengine/core/config/wrong_category_name.yml.erb +13 -0
  109. data/spec/tengine/core/config/wrong_field_name.yml.erb +12 -0
  110. data/spec/tengine/core/config/wrong_yaml.yml.erb +13 -0
  111. data/spec/tengine/core/config_spec/another_port.yml +54 -0
  112. data/spec/tengine/core/config_spec/config_with_dir_absolute_load_path.yml +16 -0
  113. data/spec/tengine/core/config_spec/config_with_dir_relative_load_path.yml +16 -0
  114. data/spec/tengine/core/config_spec/config_with_file_absolute_load_path.yml +16 -0
  115. data/spec/tengine/core/config_spec/config_with_file_relative_load_path.yml +16 -0
  116. data/spec/tengine/core/config_spec/log_config_spec.rb +235 -0
  117. data/spec/tengine/core/driveable_spec.rb +240 -0
  118. data/spec/tengine/core/driver_spec.rb +159 -0
  119. data/spec/tengine/core/dsl_loader_spec.rb +172 -0
  120. data/spec/tengine/core/dsls/uc08_if_both_a_and_b_occurs_spec.rb +35 -0
  121. data/spec/tengine/core/dsls/uc10_if_the_event_occurs_at_the_server_spec.rb +58 -0
  122. data/spec/tengine/core/dsls/uc50_commit_event_at_first_spec.rb +29 -0
  123. data/spec/tengine/core/dsls/uc52_commit_event_after_all_handler_submit_spec.rb +33 -0
  124. data/spec/tengine/core/dsls/uc52_never_commit_event_unless_all_handler_submit_spec.rb +37 -0
  125. data/spec/tengine/core/dsls/uc53_submit_outside_of_handler_spec.rb +37 -0
  126. data/spec/tengine/core/dsls/uc60_event_in_handler_spec.rb +31 -0
  127. data/spec/tengine/core/dsls/uc61_event_outside_of_handler_spec.rb +37 -0
  128. data/spec/tengine/core/dsls/uc62_session_in_driver_spec.rb +36 -0
  129. data/spec/tengine/core/dsls/uc63_session_outside_of_driver_spec.rb +35 -0
  130. data/spec/tengine/core/dsls/uc64_safety_countup_spec.rb +134 -0
  131. data/spec/tengine/core/dsls/uc70_driver_enabled_on_activation_spec.rb +39 -0
  132. data/spec/tengine/core/dsls/uc71_driver_disabled_on_activation_spec.rb +36 -0
  133. data/spec/tengine/core/dsls/uc72_setup_eventmachine_spec.rb +39 -0
  134. data/spec/tengine/core/dsls/uc80_raise_io_error_spec.rb +53 -0
  135. data/spec/tengine/core/dsls/uc81_raise_runtime_error_spec.rb +49 -0
  136. data/spec/tengine/core/event/finder_spec.rb +136 -0
  137. data/spec/tengine/core/event_exception_reportable_spec.rb +33 -0
  138. data/spec/tengine/core/event_spec.rb +161 -0
  139. data/spec/tengine/core/event_wrapper_spec.rb +35 -0
  140. data/spec/tengine/core/handler_path_spec.rb +87 -0
  141. data/spec/tengine/core/handler_spec.rb +190 -0
  142. data/spec/tengine/core/heartbeat_watcher_spec.rb +131 -0
  143. data/spec/tengine/core/io_to_logger_spec.rb +30 -0
  144. data/spec/tengine/core/kernel_spec.rb +885 -0
  145. data/spec/tengine/core/mutex_spec.rb +184 -0
  146. data/spec/tengine/core/optimistic_lock_spec.rb +55 -0
  147. data/spec/tengine/core/scheculer_spec.rb +121 -0
  148. data/spec/tengine/core/selectable_attr_spec.rb +30 -0
  149. data/spec/tengine/core/session_spec.rb +104 -0
  150. data/spec/tengine/core/setting_spec.rb +79 -0
  151. data/spec/tengine/core_spec.rb +13 -0
  152. data/spec/tengine_spec.rb +14 -0
  153. data/tengine_core.gemspec +248 -0
  154. data/tmp/log/.gitignore +1 -0
  155. data/tmp/tengined_status/.gitignore +1 -0
  156. metadata +421 -0
@@ -0,0 +1,21 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/core'
3
+
4
+ class Tengine::Core::Schedule
5
+ include Mongoid::Document
6
+ include Mongoid::Timestamps
7
+
8
+ # statuses
9
+ SCHEDULED = 0
10
+ INVALID = 1
11
+ FIRED = 2
12
+
13
+ field :event_type_name, :type => String
14
+ field :scheduled_at , :type => Time , :default => proc { Time.now }
15
+ field :status , :type => Integer, :default => SCHEDULED
16
+ field :source_name , :type => String
17
+ field :properties , :type => Hash , :default => proc { Hash.new }
18
+
19
+ index([ [:scheduled_at, Mongo::ASCENDING], [:status, Mongo::ASCENDING], ])
20
+ index([ [:status, Mongo::ASCENDING], ])
21
+ end
@@ -0,0 +1,156 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'daemons'
3
+ require 'eventmachine'
4
+ require 'mongoid'
5
+ require 'uuid'
6
+
7
+ $LOAD_PATH.push File.expand_path("../../../../lib/", __FILE__)
8
+
9
+ require 'tengine/core'
10
+ require 'tengine/event'
11
+ require 'tengine/mq'
12
+
13
+ # explicit loading
14
+ require_relative 'config/atd'
15
+ require_relative 'method_traceable'
16
+ require_relative 'schedule'
17
+ require_relative 'mongoid_fix'
18
+
19
+ class Tengine::Core::Scheduler
20
+
21
+ def initialize argv
22
+ @uuid = UUID.new.generate
23
+ @config = Tengine::Core::Config::Atd.parse argv
24
+ @daemonize_options = {
25
+ :app_name => 'tengine_atd',
26
+ :ARGV => [@config[:action]],
27
+ :ontop => !@config[:process][:daemon],
28
+ :multiple => true,
29
+ :dir_mode => :normal,
30
+ :dir => File.expand_path(@config[:process][:pid_dir]),
31
+ }
32
+
33
+ Tengine::Core::MethodTraceable.disabled = !@config[:verbose]
34
+ rescue Exception
35
+ puts "[#{$!.class.name}] #{$!.message}\n " << $!.backtrace.join("\n ")
36
+ raise
37
+ end
38
+
39
+ def sender
40
+ @sender ||= Tengine::Event::Sender.new Tengine::Mq::Suite.new(@config[:event_queue])
41
+ end
42
+
43
+ def pid
44
+ @pid ||= sprintf "process:%s/%d", ENV["MM_SERVER_NAME"], Process.pid
45
+ end
46
+
47
+ def send_last_event
48
+ sender.fire "finished.process.atd.tengine", :key => @uuid, :source_name => pid, :sender_name => pid, :occurred_at => Time.now, :level_key => :info, :keep_connection => true
49
+ sender.stop
50
+ end
51
+
52
+ def send_periodic_event
53
+ sender.fire "atd.heartbeat.tengine", :key => @uuid, :source_name => pid, :sender_name => pid, :occurred_at => Time.now, :level_key => :debug, :keep_connection => true, :retry_count => 0
54
+ end
55
+
56
+ def send_scheduled_event sched
57
+ Tengine.logger.info "Scheduled time (#{sched.scheduled_at}) has come. Now firing #{sched.event_type_name} for #{sched.source_name}"
58
+ sender.fire sched.event_type_name, :key => sched._id, :source_name => sched.source_name, :sender_name => pid, :occurred_at => Time.now, :level_key => :info, :keep_connection => true, :properties => sched.properties
59
+ end
60
+
61
+ def mark_schedule_done sched
62
+ # 複数のマシンで複数のatdが複数動いている可能性があり、その場合には複数の
63
+ # atdが同時に同じエントリに更新をかける可能性はとても高い。そのような状況
64
+ # でもエラーになってはいけない。
65
+ Tengine::Core::Schedule.where(
66
+ :_id => sched.id,
67
+ :status => Tengine::Core::Schedule::SCHEDULED
68
+ ).update_all(
69
+ :status => Tengine::Core::Schedule::FIRED
70
+ )
71
+ end
72
+
73
+ def search_for_schedule
74
+ Tengine::Core::Schedule.where(
75
+ :scheduled_at.lte => Time.now,
76
+ :status => Tengine::Core::Schedule::SCHEDULED
77
+ ).each_next_tick do |i|
78
+ yield i
79
+ end
80
+ end
81
+
82
+ def run(__file__)
83
+ case @config[:action].to_sym
84
+ when :start
85
+ start_daemon(__file__)
86
+ when :stop
87
+ stop_daemon(__file__)
88
+ when :restart
89
+ stop_daemon(__file__)
90
+ start_daemon(__file__)
91
+ end
92
+ end
93
+
94
+ def start_daemon(__file__)
95
+ pdir = File.expand_path @config[:process][:pid_dir]
96
+ fname = File.basename __file__
97
+ cwd = Dir.getwd
98
+ # Daemons.run_proc(fname, :ARGV => [@config[:action]], :multiple => true, :ontop => !@config[:process][:daemon], :dir_mode => :normal, :dir => pdir) do
99
+ Daemons.run_proc(fname, @daemonize_options) do
100
+ Dir.chdir(cwd) { self.start }
101
+ end
102
+ end
103
+
104
+ def stop_daemon(__file__)
105
+ fname = File.basename __file__
106
+ Daemons.run_proc(fname, @daemonize_options)
107
+ end
108
+
109
+ def shutdown
110
+ EM.run do
111
+ EM.cancel_timer @periodic if @periodic
112
+ send_last_event
113
+ end
114
+ end
115
+
116
+ def start
117
+ @config.setup_loggers
118
+
119
+ Mongoid.config.from_hash @config[:db]
120
+ Mongoid.config.option :persist_in_safe_mode, :default => true
121
+
122
+ require 'amqp'
123
+ Mongoid.logger = AMQP::Session.logger = Tengine.logger
124
+
125
+ EM.run do
126
+ sender.wait_for_connection do
127
+ @invalidate = EM.add_periodic_timer 1 do # !!! MAGIC NUMBER
128
+ search_for_schedule do |sched|
129
+ send_scheduled_event sched
130
+ mark_schedule_done sched
131
+ end
132
+ end
133
+ int = @config[:heartbeat][:atd][:interval].to_i
134
+ if int and int > 0
135
+ @periodic = EM.add_periodic_timer int do
136
+ send_periodic_event
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ extend Tengine::Core::MethodTraceable
144
+ method_trace(*instance_methods(false))
145
+ end
146
+
147
+ #
148
+ # Local Variables:
149
+ # mode: ruby
150
+ # coding: utf-8-unix
151
+ # indent-tabs-mode: nil
152
+ # tab-width: 4
153
+ # ruby-indent-level: 2
154
+ # fill-column: 79
155
+ # default-justification: full
156
+ # End:
@@ -0,0 +1,29 @@
1
+ require 'tengine/core'
2
+
3
+ require 'selectable_attr'
4
+
5
+ module Tengine::Core::SelectableAttr
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ::SelectableAttr::Base
10
+
11
+ class << self
12
+ def selectable_attr_with_i18n_scope(attr_name, *args, &block)
13
+ enum = selectable_attr_without_i18n_scope(attr_name, *args, &block)
14
+ enum.i18n_scope('selectable_attrs', self.name.underscore, attr_name.to_s)
15
+ enum
16
+ end
17
+ alias_method_chain :selectable_attr, :i18n_scope
18
+
19
+ def multi_selectable_attr_with_i18n_scope(attr_name, *args, &block)
20
+ enum = multi_selectable_attr_without_i18n_scope(attr_name, *args, &block)
21
+ enum.i18n_scope('selectable_attrs', self.name.underscore, attr_name.to_s)
22
+ enum
23
+ end
24
+ alias_method_chain :multi_selectable_attr, :i18n_scope
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,21 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/core'
3
+
4
+ class Tengine::Core::Session
5
+ include Mongoid::Document
6
+ include Mongoid::Timestamps
7
+ include Tengine::Core::CollectionAccessible
8
+ include Tengine::Core::OptimisticLock
9
+
10
+ field :lock_version, :type => Integer, :default => 1
11
+ field :properties, :type => Hash, :default => {}
12
+ map_yaml_accessor :properties
13
+ field :system_properties, :type => Hash, :default => {}
14
+ map_yaml_accessor :system_properties
15
+
16
+ has_one :driver, :class_name => "Tengine::Core::Driver"
17
+
18
+ # 元々の[]と[]=メソッドをオーバーライドしているので要注意
19
+ def [](key); properties[key]; end
20
+ def []=(key, value); properties[key] = value; end
21
+ end
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/core'
3
+
4
+ require 'active_support/core_ext/array/extract_options'
5
+ require 'active_support/hash_with_indifferent_access'
6
+
7
+ class Tengine::Core::SessionWrapper
8
+
9
+ def initialize(source, options = {})
10
+ @options = options || {}
11
+ @source = source
12
+ end
13
+
14
+ def system_properties
15
+ @source.system_properties
16
+ end
17
+
18
+ def [](key)
19
+ @source.properties[key.to_s]
20
+ end
21
+
22
+ def update(*args, &block)
23
+ return if @options[:ignore_update]
24
+ __update__(:properties, *args, &block)
25
+ end
26
+
27
+ def system_update(*args, &block)
28
+ __update__(:system_properties, *args, &block)
29
+ end
30
+
31
+ private
32
+ def __update__(target_name, *args, &block)
33
+ if block_given?
34
+ options = args.extract_options!
35
+ retry_count = options[:retry] || 5
36
+ idx = 1
37
+ while idx <= retry_count
38
+ values = ActiveSupport::HashWithIndifferentAccess.new(__get_properties__(target_name, idx > 0))
39
+ yield(values)
40
+ return if __find_and_modify__(target_name, values)
41
+ idx += 1
42
+ end
43
+ raise Tengine::Core::OptimisticLock::RetryOverError, "retried #{retry_count} times but failed to update"
44
+ else
45
+ properties = args.first
46
+ new_vals = __get_properties__(target_name).merge(properties.stringify_keys)
47
+ @source.send("#{target_name}=", new_vals)
48
+ @source.save!
49
+ end
50
+ end
51
+
52
+ # テストで同時に値を取得したことを再現するために、
53
+ # データを取得するメソッドで待ち合わせするフックとなるようにメソッドに分けています
54
+ def __get_properties__(target_name, reload = false)
55
+ @source.reload if reload
56
+ @source.send(target_name)
57
+ end
58
+
59
+ def __find_and_modify__(target_name, values)
60
+ result = Tengine::Core::Session.collection.find_and_modify({
61
+ :query => {:_id => @source.id, :lock_version => @source.lock_version},
62
+ :update => { target_name => values, :lock_version => @source.lock_version + 1}
63
+ })
64
+ result
65
+ end
66
+
67
+
68
+ end
@@ -0,0 +1,21 @@
1
+ require 'tengine/core'
2
+
3
+ class Tengine::Core::Setting
4
+ include Mongoid::Document
5
+ include Tengine::Core::FindByName
6
+
7
+ field :name, :type => String
8
+ field :value
9
+
10
+ validates :name, :presence => true, :uniqueness => true
11
+
12
+ index :name, :unique => true
13
+
14
+ class << self
15
+ def dsl_version
16
+ document = first(:conditions => {:name => "dsl_version"})
17
+ raise Mongoid::Errors::DocumentNotFound.new(Tengine::Core::Setting, "dsl_version") unless document
18
+ document.value
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/core'
3
+
4
+ module Tengine::Core::Validation
5
+
6
+ class Definition
7
+ attr_reader :format, :description
8
+ def initialize(format, description)
9
+ @format, @description = format.freeze, description.freeze
10
+ end
11
+ def message
12
+ "は#{description}でなければなりません"
13
+ end
14
+ def options
15
+ {:with => format, :message => message}
16
+ end
17
+ end
18
+
19
+ # ベース名
20
+ BASE_NAME = Definition.new(
21
+ /\A[A-Za-z_][\w\-]*\Z/,
22
+ "英文字またはアンダースコアから始まり、英文字、アンダースコア、ハイフンまたは数字で構成される文字列").freeze
23
+
24
+ # イベント種別名
25
+ EVENT_TYPE_NAME = Definition.new(
26
+ /\A[A-Za-z_][\w\-\.\:]*\Z/,
27
+ "英文字またはアンダースコアから始まり、英文字、アンダースコア、ハイフン、ドット、コロンまたは数字で構成される文字列").freeze
28
+
29
+ # リソース識別子
30
+ RESOURCE_IDENTIFIER_PROTOCOL_FORMAT = /\A\w+\Z/.freeze
31
+ RESOURCE_IDENTIFIER = Definition.new(
32
+ /\A#{RESOURCE_IDENTIFIER_PROTOCOL_FORMAT.source}:#{BASE_NAME.format.source}(?:\/#{BASE_NAME.format.source})*\Z/,
33
+ "'プロトコル:要素1/要素2/.../要素N'という構造を持つ文字列(プロトコルは英数字あるいはアンダースコア、要素は英数字あるいはアンダースコアかハイフン)"
34
+ ).freeze
35
+
36
+ end
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine_core'
3
+
4
+ module Tengine::Errors
5
+
6
+ # Tengineが提供するAPIによってデータが見つからないことを示す例外です。
7
+ # railsの場合、ActionDispatch::ShowExceptions.rescue_responses を使って
8
+ # 以下のように設定して使用することを想定しています。
9
+ #
10
+ # ActionDispatch::ShowExceptions.rescue_responses.update({
11
+ # "Tengine::Errors::NotFound" => :not_found,
12
+ # })
13
+ #
14
+ # see http://d.hatena.ne.jp/takihiro/20100318/1268864801
15
+ class NotFound < StandardError
16
+ end
17
+
18
+ end
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine_core'
3
+
4
+ # Tengineのイベントドライバのテストを行うためのRSpecの拡張です
5
+ module Tengine::RSpec
6
+ autoload :ContextWrapper, 'tengine/rspec/context_wrapper'
7
+ autoload :Extension , 'tengine/rspec/extension'
8
+ end
@@ -0,0 +1,51 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/rspec'
3
+
4
+ # Kernelのcontextをラップするクラスです
5
+ class Tengine::RSpec::ContextWrapper
6
+ attr_accessor :__driver__
7
+
8
+ def initialize(kernel)
9
+ @kernel = kernel
10
+ @context = @kernel.context
11
+ end
12
+
13
+ def receive(event_type_name, options = {})
14
+ mock_headers = Object.new
15
+ mock_headers.should_receive(:ack)
16
+ raw_event = Tengine::Event.new({:event_type_name => event_type_name}.update(options || {}))
17
+ @kernel.process_message(mock_headers, raw_event.to_json)
18
+ end
19
+
20
+ def should_receive(*args)
21
+ @context.should_receive(*args)
22
+ end
23
+
24
+ def should_not_receive(*args)
25
+ @context.should_not_receive(*args)
26
+ end
27
+
28
+ def should_fire(*args)
29
+ @kernel.should_receive(:fire).with(*args)
30
+ end
31
+ def should_not_fire(*args)
32
+ if args.empty?
33
+ @kernel.should_not_receive(:fire)
34
+ else
35
+ @kernel.should_not_receive(:fire).with(*args)
36
+ end
37
+ end
38
+
39
+ def __driver_class__
40
+ @__driver_class__ ||= __driver__.target_class_name.constantize
41
+ end
42
+
43
+ def __driver_object__
44
+ unless @__driver_object__
45
+ @__driver_object__ = __driver_class__.new
46
+ __driver_class__.stub(:new).and_return(@__driver_object__)
47
+ end
48
+ @__driver_object__
49
+ end
50
+
51
+ end
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/rspec'
3
+
4
+ # イベントドライバのテストのためのメソッドを追加するモジュールです。
5
+ # includeしてお使いください。
6
+ module Tengine::RSpec::Extension
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ def target_dsl(dsl_path)
11
+ before do
12
+ Tengine::Core::Driver.delete_all
13
+ Tengine::Core::Session.delete_all
14
+ @__dsl_path__ = dsl_path
15
+ @__config__ = Tengine::Core::Config::Core.new({
16
+ :tengined => { :load_path => @__dsl_path__ },
17
+ })
18
+ @__bootstrap__ = Tengine::Core::Bootstrap.new(@__config__)
19
+ @__kernel__ = @__bootstrap__.kernel
20
+ @__kernel__.bind
21
+ @__kernel__.evaluate
22
+ # @__bootstrap__.load_dsl
23
+ # @__kernel__ = Tengine::Core::Kernel.new(@__config__)
24
+ # @__kernel__.bind
25
+ @__tengine__ = Tengine::RSpec::ContextWrapper.new(@__kernel__)
26
+ end
27
+ end
28
+
29
+ def driver(driver_name)
30
+ before do
31
+ @__driver__ = Tengine::Core::Driver.first(:conditions => {:name => driver_name})
32
+ if @__driver__
33
+ session = @__driver__.session
34
+ @__session__ = Tengine::Core::SessionWrapper.new(session)
35
+ @__tengine__.__driver__ = @__driver__
36
+ else
37
+ raise "No driver named ``#{driver_name}'' was found."
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ module InstanceMethods
44
+ def session
45
+ @__session__
46
+ end
47
+
48
+ def tengine
49
+ @__tengine__
50
+ end
51
+ end
52
+
53
+ end