io_request 1.2.0 → 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.
@@ -1,112 +1,94 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module IORequest
2
- # Message to other side of IO.
4
+ # Single message. Either request or response.
3
5
  class Message
4
- # @return [Integer] ID of message.
5
- attr_reader :id
6
- alias_method :to_i, :id
7
-
8
- # @return [Hash] stored data.
9
- attr_reader :data
6
+ include Utility::WithID
7
+ # Types of messages.
8
+ TYPES = %i[request response].freeze
10
9
 
11
- # Initialize new message.
12
- #
10
+ # Create new message.
13
11
  # @param data [Hash]
14
- # @param id [Integer, nil] if +nil+ provided {Message.generate_id} will be
15
- # used to generate random id.
16
- def initialize(data, id = nil)
17
- @id = id || Message.generate_id
12
+ # @param type [Symbol] one of {TYPES} member.
13
+ # @param id [Utility::ExtendedID, String, nil] only should be filled if message is received from outside.
14
+ # @param to [Utility::ExtendedID, String, nil] if message is response, it should include integer
15
+ # of original request.
16
+ def initialize(data, type: :request, id: nil, to: nil)
18
17
  @data = data
19
- end
18
+ @type = type
19
+ @id = id.nil? ? extended_id : Utility::ExtendedID.from(id)
20
+ @to = to.nil? ? nil : Utility::ExtendedID.from(to)
20
21
 
21
- # @return [String] human-readable form.
22
- def to_s
23
- "#{self.class.name}##{@id}: #{@data.inspect}"
22
+ check_data
24
23
  end
25
24
 
26
- # @return [Integer] random numerical ID based on current time and random salt.
27
- def self.generate_id
28
- ((rand(999) + 1) * Time.now.to_f * 1000).to_i % 2**32
25
+ # Check data correctness.
26
+ def check_data
27
+ raise '@data is not a hash' unless @data.is_a? Hash
28
+ raise 'incorrect @type' unless TYPES.include? @type
29
+ raise 'incorrect @id' unless @id.is_a? Utility::ExtendedID
30
+ raise '@to not specified for response' if response? && @to.nil?
29
31
  end
30
- end
31
-
32
- # Request for server or client.
33
- class Request < Message
34
- # Amount of time to sleep before checking whether responded.
35
- JOIN_SLEEP_TIME = 0.5
36
-
37
- # @return [Integer, Response, nil] ID of response or response itself for this message.
38
- attr_reader :response
39
32
 
40
- # @!visibility private
41
- attr_writer :response
33
+ # @return [Hash]
34
+ attr_reader :data
42
35
 
43
- # Initialize new request.
44
- #
45
- # @param data [Hash]
46
- # @param response [Integer, Response, nil]
47
- # @param id [Integer, nil]
48
- def initialize(data, response = nil, id = nil)
49
- @response = response
50
- super(data, id)
51
- end
36
+ # @return [Symbol]
37
+ attr_reader :type
52
38
 
53
- # @return [String] human readable form.
54
- def to_s
55
- "#{super.to_s}; #{@response ? "Response ID: #{@response.to_i}" : "Not responded"}"
56
- end
39
+ # @return [Utility::ExtendedID]
40
+ attr_reader :id
57
41
 
58
- # Freezes thread until request is responded or until timeout expends.
59
- #
60
- # @param timeout [Integer, Float, nil] timeout size or +nil+ if no timeout.
61
- #
62
- # @return [Integer] amount of time passed
63
- def join(timeout = nil)
64
- time_passed = 0
65
- while @response.nil? && (timeout.nil? || time_passed < timeout)
66
- time_passed += (sleep JOIN_SLEEP_TIME)
67
- end
68
- time_passed
69
- end
42
+ # @return [Utility::ExtendedID]
43
+ attr_reader :to
70
44
 
71
- # Save into hash.
72
- def to_hash
73
- { type: "request", data: @data, id: @id, response: @response.to_i }
45
+ # @return [Boolean]
46
+ def request?
47
+ @type == :request
74
48
  end
75
49
 
76
- # Initialize new request from hash obtained with {Request#to_hash}.
77
- def self.from_hash(hash)
78
- Request.new(hash[:data], hash[:response], hash[:id])
50
+ # @return [Boolean]
51
+ def response?
52
+ @type == :response
79
53
  end
80
- end
81
54
 
82
- # Response to some request.
83
- class Response < Message
84
- # @return [Integer, Request] ID of initial request or request itself.
85
- attr_reader :request
86
-
87
- # Initialize new response.
88
- #
89
- # @param data [Hash]
90
- # @param request [Integer, Request]
91
- # @param id [Integer, nil]
92
- def initialize(data, request, id = nil)
93
- @request = request
94
- super(data, id)
55
+ # @return [String]
56
+ def to_s
57
+ if request?
58
+ "Request##{@id}: #{data}"
59
+ else
60
+ "Response##{@id}: #{data} to ##{@to}"
61
+ end
95
62
  end
96
63
 
97
- # @return [String] human readable form.
98
- def to_s
99
- "#{super.to_s}; Initial request ID: #{@request.to_i}"
64
+ # @return [String] binary data to be passed over IO.
65
+ def to_binary
66
+ json_string = JSON.generate({
67
+ id: @id.to_s,
68
+ type: @type.to_s,
69
+ to: @to.to_s,
70
+ data: @data
71
+ })
72
+ [json_string.size, json_string].pack("Sa#{json_string.size}")
100
73
  end
101
74
 
102
- # Save into hash.
103
- def to_hash
104
- { type: "response", data: @data, id: @id, request: @request.to_i }
75
+ # @param io_w [:write]
76
+ def write_to(io_w)
77
+ io_w.write(to_binary)
105
78
  end
106
79
 
107
- # Initialize new request from hash obtained with {Response#to_hash}.
108
- def self.from_hash(hash)
109
- Response.new(hash[:data], hash[:request], hash[:id])
80
+ # @param io_r [:read]
81
+ # @return [Message]
82
+ def self.read_from(io_r)
83
+ size = io_r.read(2).unpack1('S')
84
+ raise '0 size received' if size.zero?
85
+
86
+ json_string = io_r.read(size).unpack1("a#{size}")
87
+ msg = JSON.parse(json_string, symbolize_names: true)
88
+ Message.new(msg[:data],
89
+ id: msg[:id],
90
+ type: msg[:type].to_sym,
91
+ to: msg[:to])
110
92
  end
111
93
  end
112
94
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IORequest
4
+ # Utility methods.
5
+ module Utility
6
+ # Adds some methods to spawn new threads and join them.
7
+ # @note This module creates instance variables with prefix +@__multi_thread__+.
8
+ module MultiThread
9
+ private
10
+
11
+ # @return [Array<Thread>] array of running threads.
12
+ def __multi_thread__threads
13
+ @__multi_thread__threads ||= []
14
+ end
15
+ alias running_threads __multi_thread__threads
16
+
17
+ # @return [Mutex] threads manipulations mutex.
18
+ def __multi_thread__mutex
19
+ @__multi_thread__mutex ||= Mutex.new
20
+ end
21
+
22
+ # Runs block with provided arguments forwarded as arguments in separate thread.
23
+ # All the inline args will be passed to block.
24
+ # @param thread_name [String] thread name.
25
+ # @return [Thread]
26
+ def in_thread(*args, name: nil)
27
+ # Synchronizing addition/deletion of new threads. That's important
28
+ __multi_thread__mutex.synchronize do
29
+ new_thread = Thread.new(*args) do |*in_args|
30
+ yield(*in_args)
31
+ ensure
32
+ __multi_thread__remove_current_thread
33
+ end
34
+ __multi_thread__threads << new_thread
35
+ new_thread.name = name if name
36
+ new_thread
37
+ end
38
+ end
39
+
40
+ # Removes current thread from thread list.
41
+ def __multi_thread__remove_current_thread
42
+ __multi_thread__mutex.synchronize do
43
+ __multi_thread__threads.delete(Thread.current)
44
+ end
45
+ end
46
+
47
+ # For each running thread.
48
+ def each_thread(&block)
49
+ __multi_thread__threads.each(&block)
50
+ end
51
+
52
+ # Kills each thread.
53
+ def kill_threads
54
+ each_thread(&:kill)
55
+ each_thread(&:join)
56
+ end
57
+
58
+ # Joins each thread.
59
+ def join_threads
60
+ each_thread(&:join)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IORequest
4
+ # Utility methods.
5
+ module Utility
6
+ # Extended Id of object
7
+ class ExtendedID
8
+ include Comparable
9
+
10
+ # Create new Id based on PID, thread ID and object ID.
11
+ def initialize(pid = nil, tid = nil, oid = nil)
12
+ @pid = pid || Process.pid
13
+ @tid = tid || Thread.current.object_id
14
+ @oid = oid || object_id
15
+ end
16
+
17
+ # @return [Integer] process ID.
18
+ attr_reader :pid
19
+
20
+ # @return [Integer] thread ID.
21
+ attr_reader :tid
22
+
23
+ # @return [Integer] object ID.
24
+ attr_reader :oid
25
+
26
+ # @return [String]
27
+ def to_s
28
+ "#{@pid}##{@tid}##{@oid}"
29
+ end
30
+
31
+ # Comparison operator.
32
+ def <=>(other)
33
+ if @pid == other.pid && @tid == other.tid
34
+ @oid <=> other.oid
35
+ elsif @pid == other.pid && @tid != other.tid
36
+ @tid <=> tid
37
+ else
38
+ @pid <=> other.pid
39
+ end
40
+ end
41
+
42
+ def self.from(obj)
43
+ case obj
44
+ when ExtendedID then new(obj.pid, obj.tid, obj.oid)
45
+ when String then new(*obj.split('#').map(&:to_i))
46
+ else
47
+ raise 'unknown type'
48
+ end
49
+ end
50
+ end
51
+ # Adds special method to return object ID.
52
+ module WithID
53
+ # Identifies object in thread and process.
54
+ def __with_id__extended_id
55
+ @__with_id__extended_id ||= ExtendedID.new
56
+ end
57
+ alias extended_id __with_id__extended_id
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IORequest
4
+ # Utility methods.
5
+ module Utility
6
+ # Adds special method to identify object in log files.
7
+ module WithProgName
8
+ # Identifies object and thread it runs in.
9
+ def prog_name
10
+ "#{self.class.name}##{object_id} in Thread##{Thread.current.object_id}"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module IORequest
2
4
  # Gem version.
3
- VERSION = "1.2.0"
5
+ VERSION = '2.0.0'
4
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io_request
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fizvlad
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-04 00:00:00.000000000 Z
11
+ date: 2020-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,75 +25,75 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '13.0'
33
+ version: '5.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '13.0'
40
+ version: '5.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '5.0'
47
+ version: '13.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '5.0'
54
+ version: '13.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: logger
56
+ name: json
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.4'
61
+ version: '2.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.4'
68
+ version: '2.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: timeout-extensions
70
+ name: logger
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.1.1
75
+ version: '1.4'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.1.1
82
+ version: '1.4'
83
83
  - !ruby/object:Gem::Dependency
84
- name: json
84
+ name: timeout-extensions
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '2.0'
89
+ version: 0.1.1
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '2.0'
96
+ version: 0.1.1
97
97
  description:
98
98
  email:
99
99
  - fizvlad@mail.ru
@@ -102,9 +102,10 @@ extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
104
  - ".gitignore"
105
+ - ".rubocop.yml"
106
+ - ".rubocop_todo.yml"
105
107
  - ".travis.yml"
106
108
  - Gemfile
107
- - Gemfile.lock
108
109
  - LICENSE.txt
109
110
  - README.md
110
111
  - Rakefile
@@ -112,10 +113,12 @@ files:
112
113
  - examples/simple_example.rb
113
114
  - io_request.gemspec
114
115
  - lib/io_request.rb
116
+ - lib/io_request/authorizer.rb
115
117
  - lib/io_request/client.rb
116
- - lib/io_request/logging.rb
117
118
  - lib/io_request/message.rb
118
- - lib/io_request/utility.rb
119
+ - lib/io_request/utility/multi_thread.rb
120
+ - lib/io_request/utility/with_id.rb
121
+ - lib/io_request/utility/with_prog_name.rb
119
122
  - lib/io_request/version.rb
120
123
  homepage: https://github.com/fizvlad/io-request-rb
121
124
  licenses:
@@ -132,14 +135,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
135
  requirements:
133
136
  - - ">="
134
137
  - !ruby/object:Gem::Version
135
- version: 2.3.1
138
+ version: 2.6.5
136
139
  required_rubygems_version: !ruby/object:Gem::Requirement
137
140
  requirements:
138
141
  - - ">="
139
142
  - !ruby/object:Gem::Version
140
143
  version: '0'
141
144
  requirements: []
142
- rubygems_version: 3.0.4
145
+ rubygems_version: 3.1.0.pre2
143
146
  signing_key:
144
147
  specification_version: 4
145
148
  summary: Small gem to create JSON request/response type of connection over IO object