interprocess_attribute 0.1.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.
data/.pryrc ADDED
@@ -0,0 +1 @@
1
+ require "./lib/interprocess_attribute"
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - ruby-head
5
+ # UNIXSocket#recvmsg is not implemented on Rubinius yet.
6
+ # - rbx-19mode
7
+
8
+ notifications:
9
+ email: true
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+ group :default, :development do
3
+ gem 'rake'
4
+ gem 'yard'
5
+ gem 'redcarpet'
6
+ gem 'pry'
7
+ end
8
+ gemspec
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,9 @@
1
+ require "bundler/gem_tasks"
2
+ desc 'Run the test suite.'
3
+ task :test do
4
+ $LOAD_PATH.unshift 'lib'
5
+ Dir["test/*_test.rb"].each do |file|
6
+ require_relative file
7
+ end
8
+ end
9
+ task :default => :test
@@ -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,3 @@
1
+ class Class
2
+ include InterProcessAttribute
3
+ 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
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+ require 'pry'
3
+ require 'test/unit'
4
+ require 'interprocess_attribute'
5
+ require 'interprocess_attribute/core_ext/class'
6
+ Dir["test/support/**/*.rb"].each do |file|
7
+ require "./#{file}"
8
+ end
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: