vissen-parameterized 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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +67 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +48 -0
- data/LICENSE.txt +21 -0
- data/README.md +71 -0
- data/Rakefile +27 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/vissen/parameterized/accessor.rb +22 -0
- data/lib/vissen/parameterized/conditional.rb +61 -0
- data/lib/vissen/parameterized/dsl.rb +103 -0
- data/lib/vissen/parameterized/error.rb +10 -0
- data/lib/vissen/parameterized/global_scope.rb +49 -0
- data/lib/vissen/parameterized/graph.rb +34 -0
- data/lib/vissen/parameterized/parameter.rb +107 -0
- data/lib/vissen/parameterized/scope.rb +92 -0
- data/lib/vissen/parameterized/scope_error.rb +9 -0
- data/lib/vissen/parameterized/value/bool.rb +27 -0
- data/lib/vissen/parameterized/value/int.rb +30 -0
- data/lib/vissen/parameterized/value/real.rb +30 -0
- data/lib/vissen/parameterized/value/vec.rb +56 -0
- data/lib/vissen/parameterized/value.rb +112 -0
- data/lib/vissen/parameterized/version.rb +10 -0
- data/lib/vissen/parameterized.rb +201 -0
- data/vissen-parameterized.gemspec +39 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 61c1d3168e0c9c1b157fa61e1a45106cb6ecfe2d988855b4f15bd3716e4f979b
|
4
|
+
data.tar.gz: 2ecddf87812abfab49b494b70917e126cbd1dd3e6ad6fdceb91c8e00dcdf4840
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e5dcb1be70060630f47d467f494b2c94650fd6e95098fb75b3451113d73c81f524728e1d4bf8de2d53046af7394e51668dd1c37d98bb68bd255db2ac086980c
|
7
|
+
data.tar.gz: a0f02adc5b6d9a43772ea46a0c4ea7e6a06ad1bde65f5185c8c3c48500e6108cc6433dc5cd91c4856c9186e114b6c98a07b50c501450b3ab73d046aec4755c62
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- 'vendor/**/*'
|
4
|
+
- 'tmp/**/*'
|
5
|
+
TargetRubyVersion: 2.5
|
6
|
+
|
7
|
+
Style/FrozenStringLiteralComment:
|
8
|
+
EnforcedStyle: always
|
9
|
+
|
10
|
+
Layout/EndOfLine:
|
11
|
+
EnforcedStyle: lf
|
12
|
+
|
13
|
+
Layout/ClassStructure:
|
14
|
+
Enabled: true
|
15
|
+
Categories:
|
16
|
+
module_inclusion:
|
17
|
+
- include
|
18
|
+
- prepend
|
19
|
+
- extend
|
20
|
+
ExpectedOrder:
|
21
|
+
- module_inclusion
|
22
|
+
- constants
|
23
|
+
- public_class_methods
|
24
|
+
- initializer
|
25
|
+
- instance_methods
|
26
|
+
- protected_methods
|
27
|
+
- private_methods
|
28
|
+
|
29
|
+
Layout/IndentHeredoc:
|
30
|
+
EnforcedStyle: squiggly
|
31
|
+
|
32
|
+
Lint/AmbiguousBlockAssociation:
|
33
|
+
Exclude:
|
34
|
+
- 'test/**/*.rb'
|
35
|
+
|
36
|
+
Lint/InterpolationCheck:
|
37
|
+
Exclude:
|
38
|
+
- 'test/**/*.rb'
|
39
|
+
|
40
|
+
Metrics/BlockLength:
|
41
|
+
Exclude:
|
42
|
+
- 'Rakefile'
|
43
|
+
- '**/*.rake'
|
44
|
+
- 'test/**/*.rb'
|
45
|
+
- '*.gemspec'
|
46
|
+
|
47
|
+
Metrics/ModuleLength:
|
48
|
+
Exclude:
|
49
|
+
- 'test/**/*.rb'
|
50
|
+
|
51
|
+
Metrics/ParameterLists:
|
52
|
+
CountKeywordArgs: false
|
53
|
+
|
54
|
+
Naming/UncommunicativeMethodParamName:
|
55
|
+
AllowedNames:
|
56
|
+
- "_"
|
57
|
+
- a
|
58
|
+
- b
|
59
|
+
- x
|
60
|
+
- y
|
61
|
+
- i
|
62
|
+
- p
|
63
|
+
- n
|
64
|
+
- t
|
65
|
+
- r
|
66
|
+
- g
|
67
|
+
- to
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
vissen-parameterized (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.0)
|
10
|
+
docile (1.3.0)
|
11
|
+
json (2.1.0)
|
12
|
+
minitest (5.11.3)
|
13
|
+
parallel (1.12.1)
|
14
|
+
parser (2.5.1.0)
|
15
|
+
ast (~> 2.4.0)
|
16
|
+
powerpack (0.1.1)
|
17
|
+
rainbow (3.0.0)
|
18
|
+
rake (10.5.0)
|
19
|
+
rubocop (0.55.0)
|
20
|
+
parallel (~> 1.10)
|
21
|
+
parser (>= 2.5)
|
22
|
+
powerpack (~> 0.1)
|
23
|
+
rainbow (>= 2.2.2, < 4.0)
|
24
|
+
ruby-progressbar (~> 1.7)
|
25
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
26
|
+
ruby-progressbar (1.9.0)
|
27
|
+
simplecov (0.16.1)
|
28
|
+
docile (~> 1.1)
|
29
|
+
json (>= 1.8, < 3)
|
30
|
+
simplecov-html (~> 0.10.0)
|
31
|
+
simplecov-html (0.10.2)
|
32
|
+
unicode-display_width (1.3.2)
|
33
|
+
yard (0.9.12)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
ruby
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
bundler (~> 1.16)
|
40
|
+
minitest (~> 5.0)
|
41
|
+
rake (~> 10.0)
|
42
|
+
rubocop (~> 0.52)
|
43
|
+
simplecov (~> 0.16)
|
44
|
+
vissen-parameterized!
|
45
|
+
yard (~> 0.9)
|
46
|
+
|
47
|
+
BUNDLED WITH
|
48
|
+
1.16.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Sebastian Lindberg
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# 🥀 Vissen Parameterized
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/vissen-parameterized)
|
4
|
+
[](https://travis-ci.org/midi-visualizer/vissen-parameterized)
|
5
|
+
[](http://inch-ci.org/github/midi-visualizer/vissen-parameterized)
|
6
|
+
[](http://www.rubydoc.info/gems/vissen-parameterized/)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'vissen-parameterized'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install vissen-parameterized
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
class Doubler
|
28
|
+
include Parameterized
|
29
|
+
extend Parameterized::DSL
|
30
|
+
|
31
|
+
param input: Value::Real
|
32
|
+
output Value::Real
|
33
|
+
|
34
|
+
def call(params)
|
35
|
+
params.input * 2
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
external_value = Value::Real.new 21
|
40
|
+
|
41
|
+
doubler = Doubler.new
|
42
|
+
doubler.bind :input, external_value
|
43
|
+
|
44
|
+
# A tainted check is needed before reading the value
|
45
|
+
doubler.tainted? # => true
|
46
|
+
doubler.value # => 42.0
|
47
|
+
|
48
|
+
# Make sure untaint! is called before any value is changed
|
49
|
+
doubler.untaint!
|
50
|
+
|
51
|
+
external_value.set 4.5
|
52
|
+
|
53
|
+
# Before the taint check the old value is returned
|
54
|
+
doubler.value # => 42.0
|
55
|
+
doubler.tainted? # => true
|
56
|
+
doubler.value # => 9.0
|
57
|
+
```
|
58
|
+
|
59
|
+
## Development
|
60
|
+
|
61
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
62
|
+
|
63
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/midi-visualizer/vissen-parameterized.
|
68
|
+
|
69
|
+
## License
|
70
|
+
|
71
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rubocop/rake_task'
|
6
|
+
require 'yard'
|
7
|
+
|
8
|
+
Rake::TestTask.new(:test) do |t|
|
9
|
+
t.libs << 'test'
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.test_files = FileList['test/**/*_test.rb']
|
12
|
+
end
|
13
|
+
|
14
|
+
RuboCop::RakeTask.new(:rubocop)
|
15
|
+
|
16
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
17
|
+
t.stats_options = %w[--list-undoc]
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Generate Ruby documentation'
|
21
|
+
task doc: %w[yard]
|
22
|
+
|
23
|
+
task default: %w[test rubocop:auto_correct]
|
24
|
+
|
25
|
+
task pre_release: %i[default doc] do
|
26
|
+
sh 'bundle'
|
27
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'vissen/parameterized'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vissen
|
4
|
+
module Parameterized
|
5
|
+
# Simple proxy object for the parameter hash stored in Parameterized
|
6
|
+
# objects. It allows access to parameters that looks like `params.input`
|
7
|
+
# instead of `params[:input].value`.
|
8
|
+
class Accessor
|
9
|
+
protected
|
10
|
+
|
11
|
+
# @param parameters [Hash<Symbol, Parameter>] the parameters to provide
|
12
|
+
# access to.
|
13
|
+
def initialize(parameters)
|
14
|
+
parameters.each do |key, param|
|
15
|
+
define_singleton_method(key) { param.value }
|
16
|
+
end
|
17
|
+
|
18
|
+
freeze
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vissen
|
4
|
+
module Parameterized
|
5
|
+
# The conditional is just a specialized form of a parameterized object. It
|
6
|
+
# takes just one input and, through a given block, transforms it to a
|
7
|
+
# boolean value. The aliased method `#met?` is nothing more than syntactic
|
8
|
+
# suger for and equivalent to calling `#value`.
|
9
|
+
#
|
10
|
+
# === Usage
|
11
|
+
# The following exable sets up a conditional, binds it to a value and checks
|
12
|
+
# if the condition is met for two different values. The update proceedure is
|
13
|
+
# the same as with other parameterized objects.
|
14
|
+
#
|
15
|
+
# less_than_two = Conditional.new { |value| value < 2 }
|
16
|
+
# value = Value::Real.new 1
|
17
|
+
# less_than_two.bind :input, value
|
18
|
+
#
|
19
|
+
# less_than_two.tainted? # => true
|
20
|
+
# less_than_two.met? # => true
|
21
|
+
# less_than_two.untaint!
|
22
|
+
#
|
23
|
+
# value.write 3
|
24
|
+
# less_than_two.tainted? # => true
|
25
|
+
# less_than_two.met? # => false
|
26
|
+
#
|
27
|
+
class Conditional
|
28
|
+
include Parameterized
|
29
|
+
|
30
|
+
# @!method met?
|
31
|
+
# @return [true, false] the state of the conditional.
|
32
|
+
alias met? value
|
33
|
+
|
34
|
+
# @param input_klass [Class] the value class of the input parameter.
|
35
|
+
# @param opts (see Parameterized)
|
36
|
+
def initialize(input_klass = Value::Real, **opts)
|
37
|
+
super(parameters: { input: Parameter.new(input_klass) },
|
38
|
+
output: Value::Bool.new,
|
39
|
+
**opts)
|
40
|
+
|
41
|
+
define_singleton_method :call do |params|
|
42
|
+
yield params.input
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Forces the state of the output to the given value. The input is unbound
|
47
|
+
# and untainted to prevent it from affecting the output further.
|
48
|
+
#
|
49
|
+
# @param value [true, false] the value to force.
|
50
|
+
# @return [true] if the output was changed.
|
51
|
+
# @return [false] otherwise.
|
52
|
+
def force!(value = true)
|
53
|
+
input = @_params[:input]
|
54
|
+
input.unbind unless input.constant?
|
55
|
+
input.untaint!
|
56
|
+
|
57
|
+
@_value.write value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vissen
|
4
|
+
module Parameterized
|
5
|
+
# This module provides a DSL for defining input parameters and output values
|
6
|
+
# for parameterized objects.
|
7
|
+
#
|
8
|
+
# == Usage
|
9
|
+
#
|
10
|
+
# class Example
|
11
|
+
# extend DSL
|
12
|
+
#
|
13
|
+
# param input: Value::Vec,
|
14
|
+
# offset: Value::Real
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
module DSL
|
18
|
+
# @param key [Symbol] the parameter to add.
|
19
|
+
# @param value_klass [Class] the value type of the parameter.
|
20
|
+
# @param default [Object] the default value of the parameter.
|
21
|
+
# @return [nil]
|
22
|
+
def param(key, value_klass, default: nil)
|
23
|
+
@_params = {} unless defined? @_params
|
24
|
+
@_params[key] = [value_klass, default].freeze
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param value_klass [Class] the value class to use.
|
29
|
+
# @return [nil]
|
30
|
+
def output(value_klass)
|
31
|
+
@_output = value_klass
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Hash<Symbol, Parameter>] a new hash containing one parameter
|
36
|
+
# object for each parameter key.
|
37
|
+
def class_parameters
|
38
|
+
return {}.freeze unless defined? @_params
|
39
|
+
|
40
|
+
@_params.each_with_object({}) { |(k, v), h| h[k] = Parameter.new(*v) }
|
41
|
+
.freeze
|
42
|
+
end
|
43
|
+
|
44
|
+
# @raise [RuntimeError] if no output class has been defined.
|
45
|
+
#
|
46
|
+
# @return [Value, nil] a new instance of the value class defined using
|
47
|
+
# `#output`, or nil if nothing was defined.
|
48
|
+
def class_output
|
49
|
+
return nil unless defined? @_output
|
50
|
+
@_output.new
|
51
|
+
end
|
52
|
+
|
53
|
+
# Dynamically adds a `.new` method to the extending module (or class), if
|
54
|
+
# it is a descendent of Parameterized, that initializes the input
|
55
|
+
# parameters and the output value.
|
56
|
+
#
|
57
|
+
# @param mod [Module] the module that extended the DSL.
|
58
|
+
def self.extended(mod)
|
59
|
+
define_param_types mod
|
60
|
+
return unless mod <= Parameterized
|
61
|
+
|
62
|
+
mod.define_singleton_method :new do |*args, **opts|
|
63
|
+
super(*args,
|
64
|
+
parameters: class_parameters,
|
65
|
+
output: class_output,
|
66
|
+
**opts)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private_class_method :extended
|
71
|
+
|
72
|
+
# Defines custom param methods for each class that includes the `Value`
|
73
|
+
# module.
|
74
|
+
#
|
75
|
+
# @param mod [Module] the module to define the types on.
|
76
|
+
def self.define_param_types(mod)
|
77
|
+
Value.types.each do |klass|
|
78
|
+
# Skip unless the type is native to the library
|
79
|
+
next unless klass.name.start_with? 'Vissen::Parameterized::Value'
|
80
|
+
|
81
|
+
name = class_to_sym klass
|
82
|
+
mod.define_singleton_method name do |key, **opts|
|
83
|
+
param(key, klass, **opts)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private_class_method :define_param_types
|
89
|
+
|
90
|
+
# Converts a class name to a symbol.
|
91
|
+
#
|
92
|
+
# Vissen::Parameterized::Value::Real -> :real
|
93
|
+
#
|
94
|
+
# @param klass [Class] the class to symbolize.
|
95
|
+
# @return [Symbol] a symbolized version of the class name.
|
96
|
+
def self.class_to_sym(klass)
|
97
|
+
Value.canonicalize(klass).to_sym
|
98
|
+
end
|
99
|
+
|
100
|
+
private_class_method :class_to_sym
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vissen
|
4
|
+
module Parameterized
|
5
|
+
# The global scope is by definition a singleton and exists as the top level
|
6
|
+
# parent of all other scopes.
|
7
|
+
class GlobalScope < Scope
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
# The global scope can never die.
|
11
|
+
#
|
12
|
+
# @return [false]
|
13
|
+
def dead?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
# @see dead?
|
18
|
+
#
|
19
|
+
# @return [true]
|
20
|
+
def alive?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# @raise [RuntimeError]
|
25
|
+
def kill!
|
26
|
+
raise 'The global scope cannot be killed'
|
27
|
+
end
|
28
|
+
|
29
|
+
# The only scope that is included in the global scope is the global scope
|
30
|
+
# itself.
|
31
|
+
#
|
32
|
+
# @param scope [Object] the scope to check.
|
33
|
+
# @return [true] if the given scope is the global scope.
|
34
|
+
# @return [false] otherwise.
|
35
|
+
def include_scope?(scope)
|
36
|
+
equal?(scope)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @raise [StopIteration]
|
40
|
+
def parent
|
41
|
+
raise StopIteration
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def initialize; end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vissen
|
4
|
+
module Parameterized
|
5
|
+
# The graph implements a mechanism for updating and untainting a set of
|
6
|
+
# interconnected paramaeterized objects.
|
7
|
+
class Graph
|
8
|
+
# @raise [ScopeError] if any of the end nodes are not included in the
|
9
|
+
# scope.
|
10
|
+
#
|
11
|
+
# @param end_nodes [Array<Parameterized>] the top level parameterized
|
12
|
+
# objects.
|
13
|
+
# @param scope [Scope] the scope in which the graph (and the end nodes)
|
14
|
+
# exists.
|
15
|
+
def initialize(end_nodes, scope: GlobalScope.instance)
|
16
|
+
end_nodes.each do |node|
|
17
|
+
raise ScopeError unless scope.include? node
|
18
|
+
end
|
19
|
+
|
20
|
+
@end_nodes = end_nodes
|
21
|
+
|
22
|
+
freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
# Updates the entire graph.
|
26
|
+
def update!
|
27
|
+
@end_nodes.each(&:tainted?)
|
28
|
+
@end_nodes.each(&:untaint!)
|
29
|
+
|
30
|
+
@end_nodes.each { |node| yield node.value } if block_given?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vissen
|
4
|
+
module Parameterized
|
5
|
+
# Parameter respond to #value and can either return a locally stored
|
6
|
+
# constant or the value of a target object.
|
7
|
+
#
|
8
|
+
# === Usage
|
9
|
+
#
|
10
|
+
# parameter = Parameter.new Value::Real
|
11
|
+
# parameter.set 42
|
12
|
+
# parameter.value # => 42
|
13
|
+
#
|
14
|
+
# target = Value::Real.new 4.2
|
15
|
+
# parameter.bind target
|
16
|
+
# parameter.value # => 4.2
|
17
|
+
#
|
18
|
+
class Parameter
|
19
|
+
extend Forwardable
|
20
|
+
|
21
|
+
INSPECT_FORMAT = '#<%<name>s:0x%016<object_id>x %<type>s:%<value>s>'
|
22
|
+
private_constant :INSPECT_FORMAT
|
23
|
+
|
24
|
+
def_delegators :@target, :value, :tainted?, :untaint!, :scope
|
25
|
+
|
26
|
+
# @param value_klass [Class] the value type supported by the parameter.
|
27
|
+
# @param default_value [Object] the default constant value. It defaults
|
28
|
+
# to the default of the given value class.
|
29
|
+
def initialize(value_klass, default_value = nil)
|
30
|
+
@default = default_value.nil? ? value_klass::DEFAULT : default_value
|
31
|
+
@constant = value_klass.new @default
|
32
|
+
@target = @constant
|
33
|
+
end
|
34
|
+
|
35
|
+
# Unbinds the parameter and resets the value of the to the default of the
|
36
|
+
# value class.
|
37
|
+
#
|
38
|
+
# @return [self]
|
39
|
+
def clear!
|
40
|
+
set @default
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [false] if the parameter is bound to a value object.
|
44
|
+
# @return [true] otherwise.
|
45
|
+
def constant?
|
46
|
+
@constant.equal? @target
|
47
|
+
end
|
48
|
+
|
49
|
+
# @raise [RuntimeError] if the parameter is constant.
|
50
|
+
#
|
51
|
+
# @return [#value] the value target.
|
52
|
+
def target
|
53
|
+
raise RuntimeError if constant?
|
54
|
+
@target
|
55
|
+
end
|
56
|
+
|
57
|
+
# Writes a constant value to the parameter.
|
58
|
+
#
|
59
|
+
# @param value [Object] the value to set.
|
60
|
+
# @return [self]
|
61
|
+
def set(value)
|
62
|
+
@constant.write value
|
63
|
+
@target = @constant
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# TODO: validate the value type
|
68
|
+
#
|
69
|
+
# @param obj [#value] the value object to bind to.
|
70
|
+
# @return [self]
|
71
|
+
def bind(obj)
|
72
|
+
raise TypeError unless obj.respond_to?(:value)
|
73
|
+
@target = obj
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Copies the value of the target to the internal constant and unbinds from
|
78
|
+
# the target.
|
79
|
+
def unbind
|
80
|
+
raise 'cannot unbind constant' if constant?
|
81
|
+
set @target.value
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [Value] the value class of the parameter.
|
85
|
+
def type
|
86
|
+
@constant.class
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [String] the parameter value formated as a string wrapped either
|
90
|
+
# in `()` or `{}` depending on if the value is constant or bound.
|
91
|
+
def to_s
|
92
|
+
base = @target.to_s
|
93
|
+
constant? ? "(#{base})" : "{#{base}}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Produces a readable string representation of the parameter object.
|
97
|
+
#
|
98
|
+
# @return [String] a string representation.
|
99
|
+
def inspect
|
100
|
+
format INSPECT_FORMAT, name: self.class.name,
|
101
|
+
object_id: object_id,
|
102
|
+
type: Value.canonicalize(type),
|
103
|
+
value: to_s
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|