vissen-parameterized 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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