interprocess_attribute 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.pryrc +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +30 -0
- data/README.md +158 -0
- data/Rakefile +9 -0
- data/interprocess_attribute.gemspec +16 -0
- data/lib/interprocess_attribute/core_ext/class.rb +3 -0
- data/lib/interprocess_attribute.rb +43 -0
- data/test/interprocess_attribute_test.rb +61 -0
- data/test/setup.rb +8 -0
- metadata +81 -0
data/.pryrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "./lib/interprocess_attribute"
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
interprocess_attribute (0.1.0)
|
5
|
+
ichannel (~> 5.1.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
coderay (1.0.8)
|
11
|
+
ichannel (5.1.1)
|
12
|
+
method_source (0.8.1)
|
13
|
+
pry (0.9.10)
|
14
|
+
coderay (~> 1.0.5)
|
15
|
+
method_source (~> 0.8)
|
16
|
+
slop (~> 3.3.1)
|
17
|
+
rake (10.0.3)
|
18
|
+
redcarpet (2.2.2)
|
19
|
+
slop (3.3.3)
|
20
|
+
yard (0.8.3)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
interprocess_attribute!
|
27
|
+
pry
|
28
|
+
rake
|
29
|
+
redcarpet
|
30
|
+
yard
|
data/README.md
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
__OVERVIEW__
|
2
|
+
|
3
|
+
|
4
|
+
| Project | interprocess\_attribute
|
5
|
+
|:----------------|:--------------------------------------------------
|
6
|
+
| Homepage | https://github.com/robgleeson/interprocess_attribute
|
7
|
+
| Documentation | http://rubydoc.info/gems/interprocess_attribute/frames
|
8
|
+
| CI | [![Build Status](https://travis-ci.org/robgleeson/interprocess_attribute.png)](https://travis-ci.org/robgleeson/ichannel)
|
9
|
+
| Author | Robert Gleeson
|
10
|
+
|
11
|
+
|
12
|
+
__DESCRIPTION__
|
13
|
+
|
14
|
+
interprocess\_attribute persists attributes between processes. That's to say
|
15
|
+
you can change the 'name' attribute to "Rob" in the child process and see that
|
16
|
+
change propagate in the parent process with very little code.
|
17
|
+
|
18
|
+
Attributes are defined through the `interprocess_attribute` method. It
|
19
|
+
behaves just like the `attr_accessor` method in that it defines a getter and
|
20
|
+
a setter. The difference is that attributes set through interprocess\_attribute
|
21
|
+
persist between processes.
|
22
|
+
|
23
|
+
__EXAMPLES__
|
24
|
+
|
25
|
+
__1.__
|
26
|
+
|
27
|
+
The example shows a simple scenario of how you might use interprocess\_attribute
|
28
|
+
to persist the 'name' attribute among a parent process & a subprocess. A monkey
|
29
|
+
patch on Class is used but it is opt-in and can be avoided if you really want.
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require "interprocess_attribute"
|
33
|
+
require "interprocess_attribute/core_ext/class"
|
34
|
+
class Person
|
35
|
+
interprocess_attribute :name
|
36
|
+
end
|
37
|
+
|
38
|
+
person = Person.new
|
39
|
+
pid = fork do
|
40
|
+
person.name = "Rob"
|
41
|
+
end
|
42
|
+
Process.wait pid
|
43
|
+
p person.name # => "Rob"
|
44
|
+
```
|
45
|
+
|
46
|
+
__2.__
|
47
|
+
|
48
|
+
Monkey-patching Class is not everybodys cup of tea, so in that case there is
|
49
|
+
a module you can use with extend(…).
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require "interprocess_attribute"
|
53
|
+
class Person
|
54
|
+
extend InterProcessAttribute
|
55
|
+
interprocess_attribute :name
|
56
|
+
interprocess_attribute :age
|
57
|
+
end
|
58
|
+
|
59
|
+
person = Person.new
|
60
|
+
pid = fork do
|
61
|
+
person.name = "Rob"
|
62
|
+
person.age = 27
|
63
|
+
end
|
64
|
+
Process.wait pid
|
65
|
+
p person.name # => "Rob"
|
66
|
+
p person.age # => 27
|
67
|
+
```
|
68
|
+
__3.__
|
69
|
+
|
70
|
+
So far all these examples have defined public attributes but it might just be that
|
71
|
+
you don't want to expose attributes at all, you could just want persistence,
|
72
|
+
and to compensate for that it is possible to mark attributes as private or
|
73
|
+
protected:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
class Person
|
77
|
+
extend InterProcessAttribute
|
78
|
+
interprocess_attribute :name, {visibility: :private}
|
79
|
+
|
80
|
+
def initialize
|
81
|
+
self.name = "Rob"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
person = Person.new
|
86
|
+
person.name = "John" # NoMethodError
|
87
|
+
```
|
88
|
+
|
89
|
+
__4.__
|
90
|
+
|
91
|
+
The last example is a bit like inception: what happens when you fork inside a
|
92
|
+
fork and both of those forks call a setter? The behavior for this case is to
|
93
|
+
discard all values but the last one to be set. For example, `person.name` will
|
94
|
+
be "Rob" here:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
class Person
|
98
|
+
extend InterProcessAttribute
|
99
|
+
interprocess_attribute :name
|
100
|
+
end
|
101
|
+
|
102
|
+
person = Person.new
|
103
|
+
pid = fork do
|
104
|
+
person.name = "John"
|
105
|
+
pid = fork do
|
106
|
+
person.name = "Rob"
|
107
|
+
end
|
108
|
+
Process.wait pid
|
109
|
+
end
|
110
|
+
Process.wait pid
|
111
|
+
p person.name # => "Rob"
|
112
|
+
```
|
113
|
+
|
114
|
+
__GOTCHAS__
|
115
|
+
|
116
|
+
__1.__
|
117
|
+
|
118
|
+
Methods that mutate the receiver won't have their changes persist between
|
119
|
+
processes. You must use the setter if you want persistence. For example:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
class Person
|
123
|
+
extend InterProcessAttribute
|
124
|
+
interprocess_attribute :name
|
125
|
+
end
|
126
|
+
|
127
|
+
person = Person.new
|
128
|
+
person.name = "John"
|
129
|
+
person.name.sub! /J/, "" # does not persist
|
130
|
+
person.name = person.name.sub! /J/, "" # persists
|
131
|
+
```
|
132
|
+
|
133
|
+
__2.__
|
134
|
+
|
135
|
+
The transport of Ruby objects happens through serialization and communication
|
136
|
+
on a UNIXSocket. The serializer being used is Marshal, and it can serialize
|
137
|
+
most Ruby objects but there is a few it can't, so be wary.
|
138
|
+
|
139
|
+
__PLATFORM SUPPORT__
|
140
|
+
|
141
|
+
_supported_
|
142
|
+
|
143
|
+
* CRuby (1.9+)
|
144
|
+
|
145
|
+
_unsupported_
|
146
|
+
|
147
|
+
* CRuby 1.8
|
148
|
+
* MacRuby
|
149
|
+
* JRuby
|
150
|
+
* Rubinius (support for Rubinius will come sometime in the future).
|
151
|
+
|
152
|
+
__INSTALL__
|
153
|
+
|
154
|
+
$ gem install interprocess_attribute
|
155
|
+
|
156
|
+
__LICENSE__
|
157
|
+
|
158
|
+
MIT. See LICENSE.txt.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
Gem::Specification.new do |gem|
|
3
|
+
gem.name = "interprocess_attribute"
|
4
|
+
gem.version = "0.1.0"
|
5
|
+
gem.authors = ["Robert Gleeson"]
|
6
|
+
gem.email = ["rob@flowof.info"]
|
7
|
+
gem.description = %q{Attributes that persist between processes}
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.homepage = "https://github.com/robgleeson/interprocess_attribute"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($/)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.add_runtime_dependency 'ichannel', '~> 5.1.1'
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "ichannel"
|
2
|
+
module InterProcessAttribute
|
3
|
+
#
|
4
|
+
# @param [#to_s] name
|
5
|
+
# The name of the attribute.
|
6
|
+
#
|
7
|
+
# @param [Hash] opts
|
8
|
+
# @option opts [#to_s] :visibility
|
9
|
+
# "public", "protected", or "private"
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# class Person
|
13
|
+
# extend InterProcessAttribute
|
14
|
+
# interprocess_attribute :name, {visibility: :private}
|
15
|
+
#
|
16
|
+
# def initialize
|
17
|
+
# self.name = "Rob"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# person = Person.new
|
22
|
+
# person.name = "Rob"
|
23
|
+
# p person.name # => "Rob"
|
24
|
+
#
|
25
|
+
def interprocess_attribute(name, opts = {visibility: "public"})
|
26
|
+
class_eval do
|
27
|
+
channel = IChannel.new Marshal
|
28
|
+
define_method name do
|
29
|
+
while channel.readable?
|
30
|
+
instance_variable_set "@#{name}", channel.get
|
31
|
+
end
|
32
|
+
instance_variable_get "@#{name}"
|
33
|
+
end
|
34
|
+
send opts[:visibility], name.to_sym
|
35
|
+
|
36
|
+
define_method "#{name}=" do |value|
|
37
|
+
channel.put value
|
38
|
+
instance_variable_set "@#{name}", value
|
39
|
+
end
|
40
|
+
send opts[:visibility], "#{name}=".to_sym
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative "setup"
|
2
|
+
class InterProcessAttributeTest < Test::Unit::TestCase
|
3
|
+
class Person
|
4
|
+
interprocess_attribute :name
|
5
|
+
interprocess_attribute :age
|
6
|
+
interprocess_attribute :height, {visibility: :private}
|
7
|
+
interprocess_attribute :weight, {visibility: :protected}
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_defaults
|
11
|
+
person = Person.new
|
12
|
+
assert_equal nil, person.name
|
13
|
+
assert_equal nil, person.age
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_inception
|
17
|
+
person = Person.new
|
18
|
+
pid = fork do
|
19
|
+
person.name = "Rob"
|
20
|
+
pid = fork do
|
21
|
+
person.name = "John"
|
22
|
+
end
|
23
|
+
Process.wait pid
|
24
|
+
end
|
25
|
+
Process.wait pid
|
26
|
+
assert_equal "John", person.name
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_persistence_to_parent
|
30
|
+
person = Person.new
|
31
|
+
pid = fork do
|
32
|
+
person.name = "Rob"
|
33
|
+
person.age = 27
|
34
|
+
end
|
35
|
+
Process.wait pid
|
36
|
+
assert_equal "Rob", person.name
|
37
|
+
assert_equal 27, person.age
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_persistence_to_child
|
41
|
+
person = Person.new
|
42
|
+
person.name = "John"
|
43
|
+
pid = fork do
|
44
|
+
if person.name == "John"
|
45
|
+
person.name = true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
Process.wait pid
|
49
|
+
assert_equal true, person.name
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_visibility
|
53
|
+
is_public = Person.public_instance_methods.include? :name
|
54
|
+
assert is_public
|
55
|
+
is_private = Person.private_instance_methods.include? :height
|
56
|
+
assert is_private
|
57
|
+
is_protected = Person.protected_instance_methods.include? :weight
|
58
|
+
assert is_protected
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
data/test/setup.rb
ADDED
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: interprocess_attribute
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Robert Gleeson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ichannel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 5.1.1
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 5.1.1
|
30
|
+
description: Attributes that persist between processes
|
31
|
+
email:
|
32
|
+
- rob@flowof.info
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .pryrc
|
38
|
+
- .travis.yml
|
39
|
+
- Gemfile
|
40
|
+
- Gemfile.lock
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- interprocess_attribute.gemspec
|
44
|
+
- lib/interprocess_attribute.rb
|
45
|
+
- lib/interprocess_attribute/core_ext/class.rb
|
46
|
+
- test/interprocess_attribute_test.rb
|
47
|
+
- test/setup.rb
|
48
|
+
homepage: https://github.com/robgleeson/interprocess_attribute
|
49
|
+
licenses: []
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
hash: 3577632575707106316
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
hash: 3577632575707106316
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.23
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Attributes that persist between processes
|
78
|
+
test_files:
|
79
|
+
- test/interprocess_attribute_test.rb
|
80
|
+
- test/setup.rb
|
81
|
+
has_rdoc:
|