mementus 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 977daae88c6a50f8ef9c5eeb3252be9670cb44c8eb6bf92fe9dad743e65c919d
4
- data.tar.gz: 3ae3cc0e01f47d60bbfa5902e51c039af30958890ec2ddc389ac32f4678431af
3
+ metadata.gz: afc99c828f6a40d32923b70744019aaad68acac8ee3fdd40ab24d02ab1306826
4
+ data.tar.gz: 64067ea27ebc0ab1a8a8da6c39b9dabff5449c3a697a15a3457a5b2ed1683f0e
5
5
  SHA512:
6
- metadata.gz: 974136a95ddb6515ad23d95f8315ce95dccf5efc1796603555edaa68edf234ac7ab93bf16605624be729f3cf98f756cc12f5365b04be7974b7dfbaab849926a0
7
- data.tar.gz: 923891dbdf9bf495923724f58d96bd5e8d75a13577f4ea78dcebe26fa5b9503e2a80aa674f203e845def7a4c2a524678ad95f9fb87345f1105cad90aeefa05ce
6
+ metadata.gz: e600708448a1075bab05dca419b446a6af1fa2fd101a45805155580fbadee8489e8243ec4229d8e4dbdba755a87bc2d61906214638c1d017aa81562cf4504a16
7
+ data.tar.gz: 4852dfae2d84d5e984c61de8655d0e503c9247b4acb80c9ed3ba58abbacf1a8d3d907ca557412251a09e0b4777f24054a547c331f95378a7fa4ef60079137461
@@ -0,0 +1,26 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ test:
11
+
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby
17
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
18
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
19
+ uses: ruby/setup-ruby@v1
20
+ #uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
21
+ with:
22
+ ruby-version: 2.7
23
+ - name: Install dependencies
24
+ run: bundle install
25
+ - name: Run tests
26
+ run: bundle exec rspec
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Mementus
2
2
 
3
- [![Build Status](https://travis-ci.org/maetl/mementus.svg?branch=master)](https://travis-ci.org/maetl/mementus)
3
+ [![Build Status](https://img.shields.io/github/workflow/status/maetl/mementus/Ruby)](https://github.com/maetl/mementus/actions/workflows/ruby.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/mementus)](https://rubygems.org/gems/mementus)
5
+
6
+ In-memory graph computing library for Ruby.
7
+
8
+ ## Install
9
+
10
+ ```
11
+ gem install mementus
12
+ ```
4
13
 
5
14
  ## License
6
15
 
data/lib/mementus.rb CHANGED
@@ -26,3 +26,6 @@ require 'mementus/graph_builder'
26
26
  require 'mementus/node_builder'
27
27
  require 'mementus/edge_builder'
28
28
  require 'mementus/direction'
29
+ require 'mementus/model/type'
30
+ require 'mementus/model/validator'
31
+ require 'mementus/model/value'
@@ -0,0 +1,9 @@
1
+ module Mementus
2
+ module Model
3
+ module Type
4
+ # A placeholder for dynamically typed attributes that accept anything
5
+ class Any
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ module Mementus
2
+ module Model
3
+ # Checks values plugged into each slot and runs any required validations
4
+ # (validations not yet implemented).
5
+ #
6
+ # Current design raises errors rather than returns a boolean result.
7
+ class Validator
8
+ # @param fields_spec [Hash] defines the slots in the schema to validate against
9
+ def initialize(fields_spec)
10
+ @spec = fields_spec
11
+ end
12
+
13
+ # Checks a given set of fields to see if they match the specification.
14
+ #
15
+ # Raises an error when the given fields are invalid.
16
+ def check(fields)
17
+ missing_fields = @spec.keys.difference(fields.keys)
18
+
19
+ if missing_fields.any?
20
+ missing_fields.each do |field|
21
+ raise "wrong number of args" unless @spec[field].eql?(Type::Any)
22
+ end
23
+ end
24
+
25
+ mismatching_fields = fields.keys.difference(@spec.keys)
26
+
27
+ raise "key does not exist" if mismatching_fields.any?
28
+
29
+ fields.each do |(field, value)|
30
+ raise "wrong data type" unless value.is_a?(@spec[field]) || @spec[field].eql?(Type::Any)
31
+ end
32
+
33
+ true
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,62 @@
1
+ module Mementus
2
+ module Model
3
+ # Value object with comparison by value equality.
4
+ #
5
+ # This is a wrapper class, used as a factory for generating instances of
6
+ # Ruby structs that wraps the Struct constructor with method advice
7
+ # to handle validation (and eventually type coercion if needed).
8
+ class Value
9
+ def self.new(*slots, **fields, &block)
10
+ define(*slots, **fields, &block)
11
+ end
12
+
13
+ def self.define(*slots, **fields, &block)
14
+ # Cannot define a Value without specifying slots or fields
15
+ if slots.empty? && fields.empty?
16
+ raise ArgumentError.new("missing attribute definition")
17
+ end
18
+
19
+ # Build and validate an attribute specification matching the stdlib
20
+ # constructor convention
21
+ slots_spec, fields_spec = if fields.any?
22
+ raise ArgumentError.new("cannot use slots when field map is supplied") if slots.any?
23
+ [fields.keys, fields]
24
+ else
25
+ [slots, Hash[slots.map { |s| [s, Type::Any]}]]
26
+ end
27
+
28
+ validator = Validator.new(fields_spec)
29
+
30
+ # Define a new struct with the given specification of attributes
31
+ struct = Struct.new(*slots_spec, keyword_init: true, &block)
32
+
33
+ # Method advice wrapping the stdlib constructor
34
+ struct.define_method :initialize do |*args, **kwargs|
35
+
36
+ # Handles the original stdlib API of slot-based args if you really
37
+ # must do things this way. The keyword args API is the preferred
38
+ # approach.
39
+ attr_values = if args.any?
40
+ raise ArgumentError.new("cannot mix slots and kwargs") if kwargs.any?
41
+ Hash[slots.zip(args)]
42
+ else
43
+ kwargs
44
+ end
45
+
46
+ # Check that the provided values match the defined attributes and types
47
+ validator.check(attr_values)
48
+
49
+ # TODO: type coercion or mapping decision goes here
50
+
51
+ super(**attr_values)
52
+
53
+ # Freeze the instance before returning to caller
54
+ # TODO: if immutability becomes optional this needs to be wrapped
55
+ freeze
56
+ end
57
+
58
+ struct
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,3 +1,3 @@
1
1
  module Mementus
2
- VERSION = '0.8.3'.freeze
2
+ VERSION = '0.9.0'.freeze
3
3
  end
data/mementus.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.3"
22
- spec.add_development_dependency "rake", "~> 11.1"
23
- spec.add_development_dependency "rspec", "~> 3.4"
21
+ spec.add_development_dependency "bundler", "~> 2.0"
22
+ spec.add_development_dependency "rake", "~> 13.0"
23
+ spec.add_development_dependency "rspec", "~> 3.8"
24
24
  end
data/spec/graph_spec.rb CHANGED
@@ -123,12 +123,26 @@ describe Mementus::Graph do
123
123
  end
124
124
  end
125
125
 
126
- specify '#has_node?' do
126
+ describe '#has_node?' do
127
127
  graph = Mementus::Graph.new do
128
128
  set_node(node_1)
129
129
  end
130
130
 
131
- expect(graph.has_node?(node_1.id)).to be true
131
+ specify 'node' do
132
+ expect(graph.has_node?(node_1)).to be true
133
+ end
134
+
135
+ specify '!node' do
136
+ expect(graph.has_node?(node_2)).to be false
137
+ end
138
+
139
+ specify 'node.id' do
140
+ expect(graph.has_node?(node_1.id)).to be true
141
+ end
142
+
143
+ specify '!node.id' do
144
+ expect(graph.has_node?(node_2.id)).to be false
145
+ end
132
146
  end
133
147
 
134
148
  describe '#has_edge?' do
File without changes
@@ -0,0 +1,72 @@
1
+ describe Mementus::Model::Validator do
2
+ Validator = Mementus::Model::Validator
3
+ Type = Mementus::Model::Type
4
+
5
+ describe "schema with optional fields using any type" do
6
+ let(:anchor) do
7
+ Validator.new({
8
+ :href => String,
9
+ :title => Type::Any,
10
+ :target => Type::Any
11
+ })
12
+ end
13
+
14
+ specify "hash of attributes" do
15
+ expect(
16
+ anchor.check({:href => "https://maetl.net", :title => "maetl", :target => "_blank"})
17
+ ).to be_truthy
18
+ end
19
+
20
+ specify "skip any type attributes when not provided" do
21
+ expect(anchor.check({:href => "https://maetl.net"})).to be_truthy
22
+ end
23
+
24
+ specify "missing attribute error" do
25
+ expect {
26
+ anchor.check({})
27
+ }.to raise_error("wrong number of args")
28
+ end
29
+
30
+ specify "mismatching attribute error" do
31
+ expect {
32
+ anchor.check({:href => "https://maetl.net", :src => "https://maetl.net"})
33
+ }.to raise_error("key does not exist")
34
+ end
35
+ end
36
+
37
+ describe "type constraint schema" do
38
+ let(:rect) do
39
+ Validator.new({
40
+ :shape => String,
41
+ :x => Integer,
42
+ :y => Integer,
43
+ :w => Integer,
44
+ :h => Integer
45
+ })
46
+ end
47
+
48
+ specify "checks a flat set of attributes" do
49
+ expect(
50
+ rect.check({:shape => "rect", :x => 10, :y => 10, :w => 64, :h => 48})
51
+ ).to be_truthy
52
+ end
53
+
54
+ specify "missing attribute error" do
55
+ expect {
56
+ rect.check({:shape => "rect"})
57
+ }.to raise_error("wrong number of args")
58
+ end
59
+
60
+ specify "bad key error" do
61
+ expect {
62
+ rect.check({:mishape => "rect", :shape => "rect", :x => 10, :y => 10, :w => 64, :h => 48})
63
+ }.to raise_error("key does not exist")
64
+ end
65
+
66
+ specify "mismatching type error" do
67
+ expect {
68
+ rect.check({:shape => "rect", :x => "1", :y => "1", :w => "1", :h => "1"})
69
+ }.to raise_error("wrong data type")
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,43 @@
1
+ describe Mementus::Model::Value do
2
+ Value = Mementus::Model::Value
3
+
4
+ it "empty definition error" do
5
+ expect { Value.new }.to raise_error(ArgumentError)
6
+ end
7
+
8
+ it "constructs immutable struct" do
9
+ Color = Value.new(:name)
10
+ teal = Color.new(name: "teal")
11
+ expect { teal.name = "turquoise" }.to raise_error(FrozenError)
12
+ end
13
+
14
+ it "builds struct from slot declaration" do
15
+ Slots = Value.new(:first, :last)
16
+ slots = Slots.new(first: "hello", last: "world")
17
+ expect(slots.first).to eq("hello")
18
+ expect(slots.last).to eq("world")
19
+ end
20
+
21
+ it "builds struct from field declaration" do
22
+ Fields = Value.new(name: String, email: String)
23
+ fields = Fields.new(name: "maetl", email: "me@maetl.net")
24
+ expect(fields.name).to eq("maetl")
25
+ expect(fields.email).to eq("me@maetl.net")
26
+ end
27
+
28
+ it "compares by value" do
29
+ Weight = Value.new(:value, :unit)
30
+ w1 = Weight.new(value: 50, unit: "kgs")
31
+ w2 = Weight.new(value: 50, unit: "kgs")
32
+ expect(w1 == w2).to be(true)
33
+ expect(w1 === w2).to be(true)
34
+ expect(w1).to eq(w2)
35
+ end
36
+
37
+ it "supports ruby struct initializer" do
38
+ Length = Value.new(:size, :unit)
39
+ len = Length.new(500, "cm")
40
+ expect(len.size).to eq(500)
41
+ expect(len.unit).to eq("cm")
42
+ end
43
+ end
@@ -8,7 +8,7 @@ describe Mementus::Structure::IncidenceList do
8
8
  Mementus::Structure::IncidenceList.new
9
9
  end
10
10
 
11
- it_behaves_like 'an indexed graph data structure'
11
+ it_behaves_like 'an indexed graph data structure', supports_edge_ids: true
12
12
  it_behaves_like 'a directed graph data structure'
13
13
  it_behaves_like 'a property graph data structure'
14
14
  it_behaves_like 'a mutable graph data structure'
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- shared_examples_for "an indexed graph data structure" do
3
+ shared_examples_for "an indexed graph data structure" do |supports_edge_ids: false|
4
4
  describe '#new' do
5
5
  it 'starts with empty node list' do
6
6
  expect(structure.nodes_count).to eq(0)
@@ -52,6 +52,20 @@ shared_examples_for "an indexed graph data structure" do
52
52
 
53
53
  expect(structure.has_edge?(edge)).to be true
54
54
  end
55
+
56
+ it 'tests for the presence of a given edge by id' do
57
+ edge = Mementus::Edge.new(id: 123, from: Mementus::Node.new(id: 1), to: Mementus::Node.new(id: 2))
58
+ structure.set_edge(edge)
59
+
60
+ expect(structure.has_edge?(123)).to be true
61
+ end if supports_edge_ids
62
+
63
+ it 'tests for the presence of a given edge between nodes' do
64
+ edge = Mementus::Edge.new(from: Mementus::Node.new(id: 1), to: Mementus::Node.new(id: 2))
65
+ structure.set_edge(edge)
66
+
67
+ expect(structure.has_edge?(1, 2)).to be true
68
+ end if supports_edge_ids
55
69
  end
56
70
 
57
71
  describe '#node(id)' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mementus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - maetl
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-11 00:00:00.000000000 Z
11
+ date: 2021-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '11.1'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '11.1'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.4'
47
+ version: '3.8'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.4'
54
+ version: '3.8'
55
55
  description: Graph data structure toolkit.
56
56
  email:
57
57
  - me@maetl.net
@@ -59,8 +59,8 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - ".github/workflows/ruby.yml"
62
63
  - ".gitignore"
63
- - ".travis.yml"
64
64
  - Gemfile
65
65
  - LICENSE.txt
66
66
  - README.md
@@ -79,6 +79,9 @@ files:
79
79
  - lib/mementus/library.rb
80
80
  - lib/mementus/library/herschel_graph.rb
81
81
  - lib/mementus/library/story_graph.rb
82
+ - lib/mementus/model/type.rb
83
+ - lib/mementus/model/validator.rb
84
+ - lib/mementus/model/value.rb
82
85
  - lib/mementus/mutable_graph.rb
83
86
  - lib/mementus/mutators.rb
84
87
  - lib/mementus/node.rb
@@ -104,6 +107,9 @@ files:
104
107
  - spec/graph_spec.rb
105
108
  - spec/herschel_spec.rb
106
109
  - spec/integer_id_spec.rb
110
+ - spec/model/type_spec.rb
111
+ - spec/model/validator_spec.rb
112
+ - spec/model/value_spec.rb
107
113
  - spec/mutable_graph_spec.rb
108
114
  - spec/node_proxy_spec.rb
109
115
  - spec/node_spec.rb
@@ -121,7 +127,7 @@ homepage: https://github.com/maetl/mementus
121
127
  licenses:
122
128
  - MIT
123
129
  metadata: {}
124
- post_install_message:
130
+ post_install_message:
125
131
  rdoc_options: []
126
132
  require_paths:
127
133
  - lib
@@ -136,9 +142,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
142
  - !ruby/object:Gem::Version
137
143
  version: '0'
138
144
  requirements: []
139
- rubyforge_project:
140
- rubygems_version: 2.7.6
141
- signing_key:
145
+ rubygems_version: 3.1.2
146
+ signing_key:
142
147
  specification_version: 4
143
148
  summary: Graph data structure toolkit for Ruby applications.
144
149
  test_files:
@@ -149,6 +154,9 @@ test_files:
149
154
  - spec/graph_spec.rb
150
155
  - spec/herschel_spec.rb
151
156
  - spec/integer_id_spec.rb
157
+ - spec/model/type_spec.rb
158
+ - spec/model/validator_spec.rb
159
+ - spec/model/value_spec.rb
152
160
  - spec/mutable_graph_spec.rb
153
161
  - spec/node_proxy_spec.rb
154
162
  - spec/node_spec.rb
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.3
4
- - 2.4
5
- - 2.5
6
- - ruby-head
7
- - jruby-9.1.13.0
8
- - jruby-head
9
- before_install: gem install bundler
10
- script: bundle exec rspec