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 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
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
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
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in vissen-parameterized.gemspec
8
+ gemspec
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
+ [![Gem Version](https://badge.fury.io/rb/vissen-parameterized.svg)](https://badge.fury.io/rb/vissen-parameterized)
4
+ [![Build Status](https://travis-ci.org/midi-visualizer/vissen-parameterized.svg?branch=master)](https://travis-ci.org/midi-visualizer/vissen-parameterized)
5
+ [![Inline docs](http://inch-ci.org/github/midi-visualizer/vissen-parameterized.svg?branch=master)](http://inch-ci.org/github/midi-visualizer/vissen-parameterized)
6
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](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,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vissen
4
+ module Parameterized
5
+ # This is the top level output error class and should be subclassed by all
6
+ # other custom error classes used in this library.
7
+ class Error < StandardError
8
+ end
9
+ end
10
+ 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