tengine_event 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+ source "http://rubygems.org"
3
+
4
+ # Add dependencies required to use your gem here.
5
+ # Example:
6
+ # gem "activesupport", ">= 2.3.5"
7
+
8
+ gem "activesupport", ">= 3.0.0"
9
+ gem "tengine_support", ">= 0.3.24"
10
+
11
+ gem "uuid", "~> 2.3.4"
12
+
13
+ gem "amqp", "~> 0.8.0"
14
+
15
+ # Add dependencies to develop your gem here.
16
+ # Include everything needed to run rake, tests, features, etc.
17
+ group :development do
18
+ gem "rspec", "~> 2.6.0"
19
+ gem "yard", "~> 0.7.2"
20
+ gem "bundler", "~> 1.0.18"
21
+ gem "jeweler", "~> 1.6.4"
22
+ # gem "rcov", ">= 0"
23
+ gem "simplecov", "~> 0.5.3"
24
+ gem "ZenTest", "~> 4.6.2"
25
+ gem "ci_reporter", "~>1.6.5"
26
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,64 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ ZenTest (4.6.2)
5
+ activesupport (3.2.1)
6
+ i18n (~> 0.6)
7
+ multi_json (~> 1.0)
8
+ amq-client (0.8.7)
9
+ amq-protocol (>= 0.8.4)
10
+ eventmachine
11
+ amq-protocol (0.8.4)
12
+ amqp (0.8.4)
13
+ amq-client (~> 0.8.7)
14
+ amq-protocol (~> 0.8.4)
15
+ eventmachine
16
+ builder (3.0.0)
17
+ ci_reporter (1.6.9)
18
+ builder (>= 2.1.2)
19
+ diff-lcs (1.1.3)
20
+ eventmachine (0.12.10)
21
+ git (1.2.5)
22
+ i18n (0.6.0)
23
+ jeweler (1.6.4)
24
+ bundler (~> 1.0)
25
+ git (>= 1.2.5)
26
+ rake
27
+ macaddr (1.5.0)
28
+ systemu (>= 2.4.0)
29
+ multi_json (1.0.4)
30
+ rake (0.9.2.2)
31
+ rspec (2.6.0)
32
+ rspec-core (~> 2.6.0)
33
+ rspec-expectations (~> 2.6.0)
34
+ rspec-mocks (~> 2.6.0)
35
+ rspec-core (2.6.4)
36
+ rspec-expectations (2.6.0)
37
+ diff-lcs (~> 1.1.2)
38
+ rspec-mocks (2.6.0)
39
+ simplecov (0.5.4)
40
+ multi_json (~> 1.0.3)
41
+ simplecov-html (~> 0.5.3)
42
+ simplecov-html (0.5.3)
43
+ systemu (2.4.2)
44
+ tengine_support (0.3.24)
45
+ activesupport (>= 3.0.0)
46
+ uuid (2.3.5)
47
+ macaddr (~> 1.0)
48
+ yard (0.7.5)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ ZenTest (~> 4.6.2)
55
+ activesupport (>= 3.0.0)
56
+ amqp (~> 0.8.0)
57
+ bundler (~> 1.0.18)
58
+ ci_reporter (~> 1.6.5)
59
+ jeweler (~> 1.6.4)
60
+ rspec (~> 2.6.0)
61
+ simplecov (~> 0.5.3)
62
+ tengine_support (>= 0.3.24)
63
+ uuid (~> 2.3.4)
64
+ yard (~> 0.7.2)
data/README.rdoc ADDED
@@ -0,0 +1,21 @@
1
+ = tengine_event
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to tengine_event
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == License
16
+ tengine_event is distributed under MPL2.0 or LGPLv3 or the dual license of MPL2.0/LGPLv3
17
+
18
+ == Copyright
19
+
20
+ Copyright (c) 2011 nautilus-technologies.com
21
+
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "tengine_event"
18
+ gem.homepage = "https://github.com/tengine/tengine_event"
19
+ gem.license = "MPL2.0/LGPLv3"
20
+ gem.summary = %Q{Tengine Event API to access the queue}
21
+ gem.description = %Q{Tengine Event API to access the queue}
22
+ gem.email = "tengine@nautilus-technologies.com"
23
+ gem.authors = %w[taigou totty g-morita shyouhei akm]
24
+ gem.bindir = 'bin'
25
+ gem.executables = ['tengine_fire', 'tengine_event_sucks']
26
+ # dependencies defined in Gemfile
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rspec/core'
31
+ require 'rspec/core/rake_task'
32
+ RSpec::Core::RakeTask.new(:spec) do |spec|
33
+ spec.pattern = FileList['spec/**/*_spec.rb']
34
+ end
35
+
36
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
37
+ spec.pattern = 'spec/**/*_spec.rb'
38
+ spec.rcov = true
39
+ end
40
+
41
+ task :default => :spec
42
+
43
+ require 'yard'
44
+ YARD::Rake::YardocTask.new
45
+
46
+ require 'ci/reporter/rake/rspec'
47
+
48
+ task :sucks do
49
+ fp = File.expand_path '../bin/tengine_event_sucks', __FILE__
50
+ load fp
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.6
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'rubygems'
5
+ require 'bundler/setup'
6
+ require 'tengine_event'
7
+ require 'optparse'
8
+
9
+ # MQ �Τ���κ���¤�����Τ�
10
+ cfg = Hash.new
11
+ qn = nil
12
+ op = OptionParser.new $0 do |this|
13
+ # tengined compatible option names.
14
+ this.on '-o host', '--event-queue-connection-host=host', 'where to connect' do |arg| cfg[:host] = arg end
15
+ this.on '-p port', '--evant-queue-connection-port=port', 'where to connect', Integer do |arg| cfg[:port] = arg end
16
+ this.on '-u user', '--evant-queue-connection-user=user', 'whom to connect' do |arg| cfg[:user] = arg end
17
+ this.on '-s pass', '--evant-queue-connection-pass=pass', 'whom to connect' do |arg| cfg[:pass] = arg end
18
+ this.on '-q queue', '--evant-queue-connection-queue=queue', 'what to connect' do |arg| qn = arg end
19
+ end
20
+
21
+ op.parse! ARGV
22
+
23
+ hash = { :connection => cfg }
24
+ hash[:queue] = { :name => qn } if qn
25
+ suite = Tengine::Mq::Suite.new hash
26
+
27
+ # main loop
28
+ EM.run do
29
+ i = 0
30
+ j = false
31
+ suite.subscribe :confirm => proc{j = true} do |hdr, bdy|
32
+ hdr.ack
33
+ STDOUT.puts bdy
34
+ i += 1
35
+ end
36
+ timer = EM.add_periodic_timer 0.1 do
37
+ if j and i.zero?
38
+ EM.cancel_timer timer
39
+ suite.unsubscribe do
40
+ suite.stop
41
+ end
42
+ else
43
+ i = 0
44
+ end
45
+ end
46
+ end
47
+
48
+ Process.exit true
data/bin/tengine_fire ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'eventmachine'
5
+
6
+ __DIR__ = File.dirname(__FILE__)
7
+ $LOAD_PATH << File.expand_path('../lib', __DIR__)
8
+ require 'tengine/event'
9
+
10
+ if ARGV.empty? || ARGV.include?("-h") || ARGV.include?("--help")
11
+ puts "#{__FILE__} <event_type_name> [opt1:foo]..."
12
+ exit
13
+ end
14
+
15
+ event_type_name = ARGV.shift
16
+ options = ARGV.inject({}) do |d, arg|
17
+ key, value = *arg.split(/:/, 2)
18
+ d[key] = value
19
+ d
20
+ end
21
+
22
+ # see https://github.com/eventmachine/eventmachine/blob/master/tests/test_error_handler.rb
23
+ EM.error_handler{ |e|
24
+ puts "[error] tengine.fire Error raised during event loop: #{e.class}, #{e.message}\n"
25
+ puts "#{e.backtrace}\n"
26
+ EM.error_handler(nil)
27
+ EM.stop
28
+ }
29
+
30
+ EM.run do
31
+ if interval = options.delete('interval')
32
+ EM.add_periodic_timer(interval.to_i) do
33
+ puts "-" * 100
34
+ options[:keep_connection] = true
35
+ options[:retry_interval] = 3
36
+ Tengine::Event.fire(event_type_name, options)
37
+ end
38
+ else
39
+ EM.next_tick do
40
+ Tengine::Event.fire(event_type_name, options)
41
+ end
42
+ end
43
+ end
44
+
45
+ Signal.trap("TERM") { connection.close { EM.stop } }
46
+ Signal.trap("INT") { connection.close { EM.stop } }
@@ -0,0 +1,49 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/event'
3
+
4
+ # activemodelなどのObserverの仕組みを使ってイベントキューにモデルの
5
+ # 登録、変更、削除を通知するための実装を提供するモジュールです。
6
+ #
7
+ # http://guides.rubyonrails.org/active_record_validations_callbacks.html#observers
8
+ # http://mongoid.org/docs/callbacks/observers.html
9
+ module Tengine::Event::ModelNotifiable
10
+ def after_create(record)
11
+ fire_event(:created, record)
12
+ end
13
+
14
+ def after_update(record)
15
+ fire_event(:updated, record)
16
+ end
17
+
18
+ def after_destroy(record)
19
+ fire_event(:destroyed, record)
20
+ end
21
+
22
+ private
23
+ def fire_event(event_base, record)
24
+ event_properties = {
25
+ :class_name => record.class.name,
26
+ :attributes => record.attributes
27
+ }
28
+ if event_base == :updated
29
+ event_properties[:changes] = record.changes
30
+ end
31
+ event_sender.fire(event_type_name(event_base, record),
32
+ :level_key => :info,
33
+ :properties => event_properties
34
+ )
35
+ end
36
+
37
+ # def event_sender
38
+ # raise NotImplementedError
39
+ # end
40
+
41
+ def event_type_name(event_base, record)
42
+ "#{record.class.name}.#{event_base}.#{event_type_name_suffix}"
43
+ end
44
+
45
+ def event_type_name_suffix
46
+ self.class.name
47
+ end
48
+
49
+ end
@@ -0,0 +1,125 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/event'
3
+
4
+ require 'active_support/core_ext/array/extract_options'
5
+
6
+ # MQと到達保証について by @shyouhei on 2nd Nov., 2011.
7
+ #
8
+ # 端的に言ってAMQPプロトコルにはパケットの到達保証が、ありません。したがってAMQP::Exchangeを普通に使うだけでは、fireしたイベントがどこま
9
+ # で確実に届くかが保証されません。たとえばEventMachineのリアクターが停止してしまったとか、ピアプロセスがSEGVしたとか、TCPのセッションが
10
+ # 切れてしまったとか、ハードウエアの物理線がセミの産卵で断線したとか、様々な理由でイベントはbrokerに到達しないことがありえるし、それを検
11
+ # 知する手段がありません。
12
+ #
13
+ # この問題に対してRabbitMQは、それ単体では全体の到達保証をしませんが、以下の追加手段を提供してくれています。
14
+ #
15
+ # * RabbitMQ自体の実装の努力により、RabbitMQのサーバにデータが到達して以降は、RabbitMQが到達性を保証してくれます
16
+ #
17
+ # * AMQPプロトコルを勝手に拡張していて、RabbitMQのサーバにパケットが届いたことをackしてくれるようにできます
18
+ #
19
+ # したがってAMQPブローカーにRabbitMQを使っている限りは、MQサーバにパケットが到着したことを、クライアント側でackを読みながら確認すること
20
+ # で、全体としての到達保証が可能になるわけです。
21
+ #
22
+ # Tengine::Event::Sender#fireを実行すると、イベントを送信して、このackを確認する部分までを自動的に行います。したがって所謂
23
+ # fire-and-forgetの動作が達成されています。ただし、以下のように制限があります
24
+ #
25
+ # * AMQP gemの制約上、おおむね非同期的に動作します。つまり、fireは送信を予約するだけで、実のところ送信が終わるのは(再送等で)fireが終了し
26
+ # てからだいぶ先の話になります。
27
+ #
28
+ # * あるときだれかが EM.stop すると、それ以上はackを読めなくなり、再送信ができなくなります。
29
+ #
30
+ # * そうは言ってもEM.stopできないとプロセスが終了できないので、stop可能かどうかを調査できるようにしました(新API)。
31
+ #
32
+ # * fireメソッドの戻り値のTengine::Eventに新メソッド #transmitted? が追加になっていますので個別のイベントの送信が終わったかどうかはこ
33
+ # れで確認できます。
34
+ #
35
+ # * senderが送信中のイベント一覧は sender.pending_events で入手できます
36
+ #
37
+ # * もうsenderが送り終わったらそのままEM.stopしてよい場合(だいたいそうだと思いますが)のために、 sender.stop_after_transmission があり
38
+ # ます
39
+ #
40
+ # APIは今後も使い勝手のために追加する可能性があります
41
+
42
+ class Tengine::Event::Sender
43
+
44
+ # 現在不使用。やがて消します。
45
+ RetryError = Class.new StandardError
46
+
47
+ attr_reader :mq_suite
48
+ attr_reader :logger
49
+ attr_accessor :default_keep_connection
50
+
51
+ def initialize(*args)
52
+ options = args.extract_options!
53
+ config_or_mq_suite = args.first
54
+ @mq_suite =
55
+ case config_or_mq_suite when Tengine::Mq::Suite then
56
+ config_or_mq_suite
57
+ else
58
+ Tengine::Mq::Suite.new(options)
59
+ end
60
+ @default_keep_connection = @mq_suite.config[:sender][:keep_connection]
61
+ @logger = options[:logger] || Tengine::NullLogger.new
62
+ end
63
+
64
+ def stop(&block)
65
+ @mq_suite.stop(&block)
66
+ end
67
+
68
+ # publish an event message to AMQP exchange
69
+ # @param [String/Tengine::Event] event_or_event_type_name
70
+ # @param [Hash] options the options for attributes
71
+ # @option options [String] :key attriute key
72
+ # @option options [String] :source_name source_name
73
+ # @option options [Time] :occurred_at occurred_at
74
+ # @option options [Integer] :level level
75
+ # @option options [Symbol] :level_key level_key
76
+ # @option options [String] :sender_name sender_name
77
+ # @option options [Hash] :properties properties
78
+ # @option options [Hash] :keep_connection
79
+ # @option options [Hash] :retry_interval
80
+ # @option options [Hash] :retry_count
81
+ # @return [Tengine::Event]
82
+ def fire(event_or_event_type_name, options = {}, &block)
83
+ # @logger.info("fire(#{event_or_event_type_name.inspect}, #{options}) called")
84
+ opts = (options || {}).dup
85
+ cfg = {
86
+ :keep_connection => (opts.delete(:keep_connection) || default_keep_connection),
87
+ :retry_interval => opts.delete(:retry_interval),
88
+ :retry_count => opts.delete(:retry_count),
89
+ }
90
+ event =
91
+ case event_or_event_type_name
92
+ when Tengine::Event then event_or_event_type_name
93
+ else
94
+ Tengine::Event.new(opts.update(
95
+ :event_type_name => event_or_event_type_name.to_s))
96
+ end
97
+ @mq_suite.fire self, event, cfg, block
98
+ # @logger.info("fire(#{event_or_event_type_name.inspect}, #{options}) complete")
99
+ event
100
+ rescue Exception => e
101
+ @logger.warn("fire(#{event_or_event_type_name.inspect}, #{options}) raised [#{e.class.name}] #{e.message}")
102
+ raise e
103
+ end
104
+
105
+ def pending_events
106
+ @mq_suite.pending_events_for self
107
+ end
108
+
109
+ # fireの中で勝手に待つようにしましたので、今後不要です。
110
+ # 使っている箇所はやがて消していきましょう。
111
+ def wait_for_connection
112
+ yield
113
+ end
114
+ end
115
+
116
+ #
117
+ # Local Variables:
118
+ # mode: ruby
119
+ # coding: utf-8-unix
120
+ # indent-tabs-mode: nil
121
+ # tab-width: 4
122
+ # ruby-indent-level: 2
123
+ # fill-column: 135
124
+ # default-justification: full
125
+ # End:
@@ -0,0 +1,184 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine_event'
3
+
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'active_support/core_ext/hash/keys'
6
+ require 'active_support/json'
7
+ require 'uuid'
8
+
9
+ # Serializable Class of object to send to an MQ or to receive from MQ.
10
+ class Tengine::Event
11
+
12
+ autoload :Sender, 'tengine/event/sender'
13
+ autoload :ModelNotifiable, 'tengine/event/model_notifiable'
14
+
15
+ class << self
16
+ # see Tengine::Event::Sender#fire
17
+ def fire(*args, &block)
18
+ default_sender.fire(*args, &block)
19
+ end
20
+
21
+ def config; @config ||= {}; end
22
+ def config=(v); @config = v; end
23
+
24
+ attr_writer :mq_suite
25
+ def mq_suite; @mq_suite ||= Tengine::Mq::Suite.new(config); end
26
+
27
+ attr_writer :default_sender
28
+ def default_sender
29
+ @default_sender ||= Tengine::Event::Sender.new(mq_suite)
30
+ end
31
+
32
+ def uuid_gen
33
+ # uuidtools と uuid のどちらが良いかは以下のサイトを参照して uuid を使うようにしました。
34
+ # http://d.hatena.ne.jp/kiwamu/20090205/1233826235
35
+ @uuid_gen ||= ::UUID.new
36
+ end
37
+
38
+ # jsonの文字列からTengine::Eventのオブジェクトを解釈して生成します
39
+ def parse(str)
40
+ case raw_parsed = JSON.parse(str)
41
+ when Hash then
42
+ new(raw_parsed)
43
+ when Array then
44
+ raw_parsed.map{|hash| new(hash)}
45
+ else
46
+ end
47
+ end
48
+
49
+ # @attribute
50
+ # host_nameが実行するコマンド。デフォルトでは hostname。
51
+ def host_name_command; @host_name_command ||= "hostname"; end
52
+ attr_writer :host_name_command
53
+
54
+ # ホスト名を取得する
55
+ # 内部ではhost_name_commandで指定されたコマンドを実行しています。
56
+ # @return [String]
57
+ def host_name
58
+ `#{host_name_command}`.strip
59
+ end
60
+
61
+ # source_nameが指定されていない場合に設定される文字列を返します
62
+ # config[:default_source_name] に値が設定されていなかったらhost_nameの値が使用されます
63
+ def default_source_name; config[:default_source_name] || "#{host_name}/#{Process.pid}"; end
64
+
65
+ # sender_nameが指定されていない場合に設定される文字列を返します
66
+ # config[:default_sender_name] に値が設定されていなかったらhost_nameの値が使用されます
67
+ def default_sender_name; config[:default_sender_name] || "#{host_name}/#{Process.pid}"; end
68
+
69
+ # levelが指定されていない場合に設定される文字列を返します
70
+ # config[:default_level] に値が設定されていなかったらhost_nameの値が使用されます
71
+ def default_level
72
+ LEVELS_INV[(config[:default_level_key] || :info).to_sym]
73
+ end
74
+ end
75
+
76
+ # constructor
77
+ # @param [Hash] attrs the options for attributes
78
+ # @option attrs [String] :key attriute key
79
+ # @option attrs [String] :event_type_name event_type_name
80
+ # @option attrs [String] :source_name source_name
81
+ # @option attrs [Time] :occurred_at occurred_at
82
+ # @option attrs [Integer] :level level
83
+ # @option attrs [Symbol] :level_key level_key
84
+ # @option attrs [String] :sender_name sender_name
85
+ # @option attrs [Hash] :properties properties
86
+ # @return [Tengine::Event]
87
+ def initialize(attrs = nil)
88
+ if attrs
89
+ raise ArgumentError, "attrs must be a Hash but was #{attrs.inspect}" unless attrs.is_a?(Hash)
90
+ attrs.each do |key, value|
91
+ send("#{key}=", value)
92
+ end
93
+ end
94
+ klass = self.class
95
+ @key ||= klass.uuid_gen.generate # Stringを返す
96
+ @source_name ||= klass.default_source_name
97
+ @sender_name ||= klass.default_sender_name
98
+ @level ||= klass.default_level
99
+ @occurred_at ||= Time.now.utc
100
+ end
101
+
102
+ # @attribute
103
+ # キー。インスタンス生成時に同じ意味のイベントには同じキーが割り振られます。
104
+ attr_accessor :key
105
+
106
+ # @attribute
107
+ # イベント種別名。
108
+ attr_reader :event_type_name
109
+ def event_type_name=(v); @event_type_name = v.nil? ? nil : v.to_s; end
110
+
111
+ # @attribute
112
+ # イベントの発生源名。
113
+ attr_reader :source_name
114
+ def source_name=(v); @source_name = v.nil? ? nil : v.to_s; end
115
+
116
+ # @attribute
117
+ # イベントの発生日時。
118
+ attr_accessor :occurred_at
119
+ def occurred_at=(v)
120
+ case v
121
+ when nil then @occurred_at = nil
122
+ when Time then @occurred_at = v.utc
123
+ when String then
124
+ @occurred_at = v.respond_to?(:to_time) ? v.to_time : Time.respond_to?(:parse) ? Time.parse(v) : v
125
+ else
126
+ raise ArgumentError, "occurred_at must be a Time but was #{v.inspect}" unless v.is_a?(Time)
127
+ end
128
+ end
129
+
130
+ # from level to level_key
131
+ LEVELS = {
132
+ 0 => :gr_heartbeat,
133
+ 1 => :debug,
134
+ 2 => :info,
135
+ 3 => :warn,
136
+ 4 => :error,
137
+ 5 => :fatal,
138
+ }.freeze
139
+
140
+ # from level_key to level
141
+ LEVELS_INV = LEVELS.invert.freeze
142
+
143
+ # @attribute
144
+ # イベントの通知レベル
145
+ attr_accessor :level
146
+
147
+ # @attribute
148
+ # イベントの通知レベルキー
149
+ # :gr_heartbeat/:debug/:info/:warn/:error/:fatal
150
+ def level_key; LEVELS[level];end
151
+ def level_key=(v); self.level = LEVELS_INV[v.to_sym]; end
152
+
153
+ # @attribute
154
+ # イベントの送信者名。
155
+ attr_reader :sender_name
156
+ def sender_name=(v); @sender_name = v.nil? ? nil : v.to_s; end
157
+
158
+
159
+ # @attribute
160
+ # プロパティ。他の属性だけでは表現できない諸属性を格納するHashです。
161
+ def properties
162
+ @properties ||= {}
163
+ end
164
+
165
+ def properties=(hash)
166
+ @properties = (hash || {}).stringify_keys
167
+ end
168
+
169
+ ATTRIBUTE_NAMES = [:event_type_name, :key, :source_name, :occurred_at, :level, :sender_name, :properties].freeze
170
+
171
+ # @return [Hash] attributes of this object
172
+ def attributes
173
+ ATTRIBUTE_NAMES.inject({}) do |d, attr_name|
174
+ v = send(attr_name)
175
+ d[attr_name] = v unless v.blank?
176
+ d
177
+ end
178
+ end
179
+
180
+ def transmitted?
181
+ not Tengine::Mq::Suite.pending?(self)
182
+ end
183
+
184
+ end