invariable 0.1.0 → 0.1.5
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 +4 -4
- data/.yardopts +1 -1
- data/README.md +4 -4
- data/lib/invariable/version.rb +1 -1
- data/lib/invariable.rb +13 -13
- metadata +6 -76
- data/.gitignore +0 -6
- data/gems.rb +0 -4
- data/invariable.gemspec +0 -35
- data/rakefile.rb +0 -12
- data/samples/http_options.rb +0 -90
- data/samples/person.rb +0 -49
- data/spec/helper.rb +0 -12
- data/spec/invariable_include_spec.rb +0 -97
- data/spec/invariable_new_spec.rb +0 -53
- data/spec/invariable_spec.rb +0 -291
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16fb490a9818d273fc7871107dbe7f3e0c6c2bd8c4e3900c4d72e612e2979b53
|
4
|
+
data.tar.gz: 827067332845002a0a6d2c58782ba664d393a19cf0177160e4ae7f9dfb005357
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 188bd0745fd85c54e7a41c9f0d5efd6e4ae2635791214c86d021791142bf5eacf1311b1839577d7f47e84966a903d86748929ee5efa5b5c0942f127a8fd39cc9
|
7
|
+
data.tar.gz: 2bbbf4d596a92b8e13f10a445ad5cedd48c989cdd4ceec065f20c5385da7b0240b0748f225f5efb35e747328ba3cc1fad43ab9292bbb9d34607f759200152e3e
|
data/.yardopts
CHANGED
data/README.md
CHANGED
@@ -7,7 +7,7 @@ An Invariable can be created explicitly as a Class like a Struct. Or existing cl
|
|
7
7
|
|
8
8
|
- Gem: [rubygems.org](https://rubygems.org/gems/invariable)
|
9
9
|
- Source: [github.com](https://github.com/mblumtritt/invariable)
|
10
|
-
- Help: [rubydoc.info](https://rubydoc.info/
|
10
|
+
- Help: [rubydoc.info](https://rubydoc.info/gems/invariable)
|
11
11
|
|
12
12
|
## Sample
|
13
13
|
|
@@ -34,13 +34,13 @@ john.dig(:address, :city) #=> "Anytown"
|
|
34
34
|
|
35
35
|
```
|
36
36
|
|
37
|
-
For more samples see [the samples dir](
|
37
|
+
For more samples see [the samples dir](./examples)
|
38
38
|
|
39
39
|
## Installation
|
40
40
|
|
41
|
-
Use [Bundler](http://gembundler.com/) to use
|
41
|
+
Use [Bundler](http://gembundler.com/) to use Invariiable in your own project:
|
42
42
|
|
43
|
-
|
43
|
+
Include in your `Gemfile`:
|
44
44
|
|
45
45
|
```ruby
|
46
46
|
gem 'invariable'
|
data/lib/invariable/version.rb
CHANGED
data/lib/invariable.rb
CHANGED
@@ -36,12 +36,12 @@ module Invariable
|
|
36
36
|
# @!method attributes(*names, **defaults)
|
37
37
|
# Defines new attributes
|
38
38
|
# @param names [Array<Symbol>] attribute names
|
39
|
-
# @param defaults [
|
39
|
+
# @param defaults [{Symbol => Object, Class}] attribute names with default
|
40
40
|
# values
|
41
|
-
# @return [Array<
|
41
|
+
# @return [Array<Symbol>] names of defined attributes
|
42
42
|
#
|
43
43
|
# @!method member?(name)
|
44
|
-
# @return [Boolean]
|
44
|
+
# @return [Boolean] whether the given name is a valid attribute name for
|
45
45
|
# this class
|
46
46
|
#
|
47
47
|
|
@@ -77,7 +77,7 @@ module Invariable
|
|
77
77
|
# Person.members #=> [:name, :last_name, :city, :zip, :street]
|
78
78
|
#
|
79
79
|
# @param names [Array<Symbol>] attribute names
|
80
|
-
# @param defaults [
|
80
|
+
# @param defaults [{Symbol => Object, Class}] attribute names with default
|
81
81
|
# values
|
82
82
|
# @yieldparam new_class [Class] the created class
|
83
83
|
#
|
@@ -128,7 +128,7 @@ module Invariable
|
|
128
128
|
# This means that the given object needs to implement the same attributes and
|
129
129
|
# all it's attribute values have to be equal.
|
130
130
|
#
|
131
|
-
# @return [Boolean]
|
131
|
+
# @return [Boolean] whether the attribute values are equal
|
132
132
|
#
|
133
133
|
def ==(other)
|
134
134
|
@__attr__.each_pair do |k, v|
|
@@ -223,7 +223,7 @@ module Invariable
|
|
223
223
|
# Compares its class and all attributes of itself with the class and
|
224
224
|
# attributes of a given other Object.
|
225
225
|
#
|
226
|
-
# @return [Boolean]
|
226
|
+
# @return [Boolean] whether the classes and each attribute value are equal
|
227
227
|
#
|
228
228
|
# @see ==
|
229
229
|
#
|
@@ -233,7 +233,7 @@ module Invariable
|
|
233
233
|
|
234
234
|
# @!visibility private
|
235
235
|
def hash
|
236
|
-
(to_a << self.class).hash
|
236
|
+
@__hash__ ||= (to_a << self.class).hash
|
237
237
|
end
|
238
238
|
|
239
239
|
#
|
@@ -246,7 +246,7 @@ module Invariable
|
|
246
246
|
alias to_s inspect
|
247
247
|
|
248
248
|
#
|
249
|
-
# @return [Boolean]
|
249
|
+
# @return [Boolean] whether the given name is a valid attribute name
|
250
250
|
#
|
251
251
|
def member?(name)
|
252
252
|
@__attr__.key?(name)
|
@@ -284,10 +284,10 @@ module Invariable
|
|
284
284
|
|
285
285
|
#
|
286
286
|
# @overload to_h
|
287
|
-
# @return [
|
287
|
+
# @return [{Symbol => Object}] names and values of all attributes
|
288
288
|
#
|
289
289
|
# @overload to_h(compact: true)
|
290
|
-
# @return [
|
290
|
+
# @return [{Symbol => Object}] names and values of all attributes which
|
291
291
|
# are not `nil` and which are not empty Invariable results
|
292
292
|
#
|
293
293
|
# @overload to_h(&block)
|
@@ -297,7 +297,7 @@ module Invariable
|
|
297
297
|
# @yieldparam [Object] value the attribute value
|
298
298
|
# @yieldreturn [Array<Symbol,Object>] the pair to be stored in the result
|
299
299
|
#
|
300
|
-
# @return [
|
300
|
+
# @return [{Object => Object}] pairs returned by the `block`
|
301
301
|
#
|
302
302
|
def to_h(compact: false, &block)
|
303
303
|
return to_compact_h if compact
|
@@ -308,7 +308,7 @@ module Invariable
|
|
308
308
|
#
|
309
309
|
# Updates all given attributes.
|
310
310
|
#
|
311
|
-
# @return [Invariable] a new updated instance
|
311
|
+
# @return [Invariable] a new updated instance
|
312
312
|
def update(attributes)
|
313
313
|
opts = {}
|
314
314
|
@__attr__.each_pair do |k, v|
|
@@ -318,7 +318,7 @@ module Invariable
|
|
318
318
|
end
|
319
319
|
|
320
320
|
#
|
321
|
-
# @return [Array<Object>] Array whose elements are the
|
321
|
+
# @return [Array<Object>] Array whose elements are the attributes of self at
|
322
322
|
# the given Integer indexes
|
323
323
|
def values_at(...)
|
324
324
|
@__attr__.values.values_at(...)
|
metadata
CHANGED
@@ -1,71 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: invariable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Blumtritt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: yard
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
11
|
+
date: 2022-07-31 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
69
13
|
description: |
|
70
14
|
An Invariable bundles a number of read-only attributes.
|
71
15
|
It can be used like a Hash as well as an Array.
|
@@ -77,28 +21,18 @@ extra_rdoc_files:
|
|
77
21
|
- README.md
|
78
22
|
- LICENSE
|
79
23
|
files:
|
80
|
-
- ".gitignore"
|
81
24
|
- ".yardopts"
|
82
25
|
- LICENSE
|
83
26
|
- README.md
|
84
|
-
- gems.rb
|
85
|
-
- invariable.gemspec
|
86
27
|
- lib/invariable.rb
|
87
28
|
- lib/invariable/version.rb
|
88
|
-
- rakefile.rb
|
89
|
-
- samples/http_options.rb
|
90
|
-
- samples/person.rb
|
91
|
-
- spec/helper.rb
|
92
|
-
- spec/invariable_include_spec.rb
|
93
|
-
- spec/invariable_new_spec.rb
|
94
|
-
- spec/invariable_spec.rb
|
95
29
|
homepage: https://github.com/mblumtritt/invariable
|
96
30
|
licenses:
|
97
31
|
- BSD-3-Clause
|
98
32
|
metadata:
|
99
33
|
source_code_uri: https://github.com/mblumtritt/invariable
|
100
34
|
bug_tracker_uri: https://github.com/mblumtritt/invariable/issues
|
101
|
-
documentation_uri: https://rubydoc.info/
|
35
|
+
documentation_uri: https://rubydoc.info/gems/invariable
|
102
36
|
post_install_message:
|
103
37
|
rdoc_options: []
|
104
38
|
require_paths:
|
@@ -114,12 +48,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
48
|
- !ruby/object:Gem::Version
|
115
49
|
version: '0'
|
116
50
|
requirements: []
|
117
|
-
rubygems_version: 3.3.
|
51
|
+
rubygems_version: 3.3.7
|
118
52
|
signing_key:
|
119
53
|
specification_version: 4
|
120
54
|
summary: The Invariable data class for Ruby.
|
121
|
-
test_files:
|
122
|
-
- spec/helper.rb
|
123
|
-
- spec/invariable_include_spec.rb
|
124
|
-
- spec/invariable_new_spec.rb
|
125
|
-
- spec/invariable_spec.rb
|
55
|
+
test_files: []
|
data/.gitignore
DELETED
data/gems.rb
DELETED
data/invariable.gemspec
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative './lib/invariable/version'
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = 'invariable'
|
7
|
-
spec.version = Invariable::VERSION
|
8
|
-
spec.required_ruby_version = '>= 2.7.0'
|
9
|
-
|
10
|
-
spec.author = 'Mike Blumtritt'
|
11
|
-
spec.summary = 'The Invariable data class for Ruby.'
|
12
|
-
spec.description = <<~description
|
13
|
-
An Invariable bundles a number of read-only attributes.
|
14
|
-
It can be used like a Hash as well as an Array.
|
15
|
-
It supports subclassing and pattern matching.
|
16
|
-
description
|
17
|
-
|
18
|
-
spec.homepage = 'https://github.com/mblumtritt/invariable'
|
19
|
-
spec.license = 'BSD-3-Clause'
|
20
|
-
spec.metadata.merge!(
|
21
|
-
'source_code_uri' => 'https://github.com/mblumtritt/invariable',
|
22
|
-
'bug_tracker_uri' => 'https://github.com/mblumtritt/invariable/issues',
|
23
|
-
'documentation_uri' => 'https://rubydoc.info/github/mblumtritt/invariable'
|
24
|
-
)
|
25
|
-
|
26
|
-
spec.add_development_dependency 'bundler'
|
27
|
-
spec.add_development_dependency 'rake'
|
28
|
-
spec.add_development_dependency 'rspec'
|
29
|
-
spec.add_development_dependency 'yard'
|
30
|
-
|
31
|
-
all_files = Dir.chdir(__dir__) { `git ls-files -z`.split(0.chr) }
|
32
|
-
spec.test_files = all_files.grep(%r{^spec/})
|
33
|
-
spec.files = all_files - spec.test_files
|
34
|
-
spec.extra_rdoc_files = %w[README.md LICENSE]
|
35
|
-
end
|
data/rakefile.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'rake/clean'
|
4
|
-
require 'bundler/gem_tasks'
|
5
|
-
require 'rspec/core/rake_task'
|
6
|
-
require 'yard'
|
7
|
-
|
8
|
-
$stdout.sync = $stderr.sync = true
|
9
|
-
CLOBBER << 'prj' << '.yardoc' << 'doc'
|
10
|
-
task(:default) { exec('rake --tasks') }
|
11
|
-
RSpec::Core::RakeTask.new { |task| task.ruby_opts = %w[-w] }
|
12
|
-
YARD::Rake::YardocTask.new { |task| task.stats_options = %w[--list-undoc] }
|
data/samples/http_options.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
#
|
3
|
-
# Sample to use Invariables as complex options.
|
4
|
-
# See the options used for Net::HTTP.start to refer the sample.
|
5
|
-
#
|
6
|
-
|
7
|
-
require_relative '../lib/invariable'
|
8
|
-
|
9
|
-
#
|
10
|
-
# HTTP Options
|
11
|
-
# HTTPOptions#to_h is used to generate the options Hash provided to
|
12
|
-
# Net::HTTP.start (see there).
|
13
|
-
#
|
14
|
-
class HTTPOptions
|
15
|
-
include Invariable
|
16
|
-
|
17
|
-
attributes :open_timeout,
|
18
|
-
:read_timeout,
|
19
|
-
:write_timeout,
|
20
|
-
:continue_timeout,
|
21
|
-
:keep_alive_timeout,
|
22
|
-
:close_on_empty_response
|
23
|
-
|
24
|
-
attribute proxy: Invariable.new(:from_env, :address, :port, :user, :pass)
|
25
|
-
|
26
|
-
attribute ssl:
|
27
|
-
Invariable.new(
|
28
|
-
:ca_file,
|
29
|
-
:ca_path,
|
30
|
-
:cert,
|
31
|
-
:cert_store,
|
32
|
-
:ciphers,
|
33
|
-
:extra_chain_cert,
|
34
|
-
:key,
|
35
|
-
:timeout,
|
36
|
-
:version,
|
37
|
-
:min_version,
|
38
|
-
:max_version,
|
39
|
-
:verify_callback,
|
40
|
-
:verify_depth,
|
41
|
-
:verify_mode,
|
42
|
-
:verify_hostname
|
43
|
-
)
|
44
|
-
|
45
|
-
#
|
46
|
-
# Superseded to add some magic and to flatten the Hash provided to eg.
|
47
|
-
# Net::HTTP.start.
|
48
|
-
#
|
49
|
-
def to_h
|
50
|
-
# the compact option allows to skip all values of nil and all empty
|
51
|
-
# Invariable values
|
52
|
-
result = super(compact: true)
|
53
|
-
|
54
|
-
# flatten the SSL options:
|
55
|
-
ssl = result.delete(:ssl)
|
56
|
-
if ssl
|
57
|
-
# prefix two options:
|
58
|
-
ssl[:ssl_timeout] = ssl.delete(:timeout) if ssl.key?(:timeout)
|
59
|
-
ssl[:ssl_version] = ssl.delete(:version) if ssl.key?(:version)
|
60
|
-
result.merge!(ssl)
|
61
|
-
|
62
|
-
# automagic :)
|
63
|
-
result[:use_ssl] = true
|
64
|
-
end
|
65
|
-
|
66
|
-
# flatten the proxy options and prefix the keys
|
67
|
-
proxy = result.delete(:proxy)
|
68
|
-
if proxy
|
69
|
-
result.merge!(proxy.transform_keys! { |key| "proxy_#{key}".to_sym })
|
70
|
-
end
|
71
|
-
|
72
|
-
result
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
puts '- create a sample'
|
77
|
-
sample =
|
78
|
-
HTTPOptions.new(
|
79
|
-
open_timeout: 2,
|
80
|
-
read_timeout: 2,
|
81
|
-
write_timeout: 2,
|
82
|
-
ssl: {
|
83
|
-
timeout: 2,
|
84
|
-
min_version: :TLS1_2
|
85
|
-
},
|
86
|
-
proxy: {
|
87
|
-
from_env: true
|
88
|
-
}
|
89
|
-
)
|
90
|
-
p sample.to_h
|
data/samples/person.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
#
|
3
|
-
# This sample shows the different aspects of Invariable.
|
4
|
-
#
|
5
|
-
|
6
|
-
require_relative '../lib/invariable'
|
7
|
-
|
8
|
-
#
|
9
|
-
# Person is a sample class which is combined from primitives as well as an
|
10
|
-
# anonymous Invariable class used for the address attribute.
|
11
|
-
#
|
12
|
-
class Person
|
13
|
-
include Invariable
|
14
|
-
attributes :name, :last_name, address: Invariable.new(:city, :zip, :street)
|
15
|
-
|
16
|
-
def full_name
|
17
|
-
"#{name} #{last_name}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_s
|
21
|
-
address.to_a.unshift(full_name).compact.join(', ')
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
puts '- we can check the members of the class'
|
26
|
-
p Person.members #=> [:name, :last_name, :address]
|
27
|
-
p Person.member?(:last_name) #=> true
|
28
|
-
|
29
|
-
puts '- create a person record'
|
30
|
-
john = Person.new(name: 'John', last_name: 'Doe')
|
31
|
-
puts john #=> "John Doe"
|
32
|
-
|
33
|
-
puts '- we can check the members of the instance'
|
34
|
-
p john.members #=> [:name, :last_name, :address]
|
35
|
-
p john.member?(:last_name) #=> true
|
36
|
-
|
37
|
-
puts '- the address members are nil'
|
38
|
-
p john.address.city #=> nil
|
39
|
-
|
40
|
-
puts '- converted to an compact Hash the address is skipped'
|
41
|
-
p john.to_h(compact: true) #=> {:name=>"John", :last_name=>"Doe"}
|
42
|
-
|
43
|
-
puts '- update the record with an address'
|
44
|
-
john =
|
45
|
-
john.update(address: { street: '123 Main St', city: 'Anytown', zip: '45678' })
|
46
|
-
|
47
|
-
puts '- the city is assigned now'
|
48
|
-
p john.dig(:address, :city) #=> "Anytown"
|
49
|
-
puts john #=> John Doe, Anytown, 45678, 123 Main St
|
data/spec/helper.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'rspec/core'
|
4
|
-
require_relative '../lib/invariable'
|
5
|
-
|
6
|
-
$stdout.sync = $stderr.sync = true
|
7
|
-
|
8
|
-
RSpec.configure do |config|
|
9
|
-
config.disable_monkey_patching!
|
10
|
-
config.warnings = true
|
11
|
-
config.order = :random
|
12
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative 'helper'
|
3
|
-
|
4
|
-
RSpec.describe 'include Invariable' do
|
5
|
-
let(:invariable) do
|
6
|
-
Class.new do
|
7
|
-
include Invariable
|
8
|
-
attribute :name
|
9
|
-
attribute :last_name
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'defines all attributes' do
|
14
|
-
expect(invariable.members).to eq %i[name last_name]
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'initializes the attributes' do
|
18
|
-
instance = invariable.new(name: 'John', last_name: 'Doe')
|
19
|
-
expect(instance.name).to eq 'John'
|
20
|
-
expect(instance.last_name).to eq 'Doe'
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'initializes only given attributes' do
|
24
|
-
instance = invariable.new(last_name: 'Doe')
|
25
|
-
expect(instance.name).to be_nil
|
26
|
-
expect(instance.last_name).to eq 'Doe'
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'ignores unknown attributes' do
|
30
|
-
expect {
|
31
|
-
invariable.new(foo: 42, last_name: 'Doe', ignored: true)
|
32
|
-
}.not_to raise_error
|
33
|
-
end
|
34
|
-
|
35
|
-
context 'when defining an already defined attribute' do
|
36
|
-
it 'raises an exception' do
|
37
|
-
expect do
|
38
|
-
Class.new do
|
39
|
-
include Invariable
|
40
|
-
attribute :name
|
41
|
-
attribute :name
|
42
|
-
end
|
43
|
-
end.to raise_error(NameError, 'attribute already defined - name')
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context 'when used in sub-classing' do
|
48
|
-
let(:invariable) { Class.new(base_class) { attributes :street, :city } }
|
49
|
-
let(:base_class) do
|
50
|
-
Class.new do
|
51
|
-
include Invariable
|
52
|
-
attributes :name, :last_name
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'defines all attributes' do
|
57
|
-
expect(invariable.members).to eq %i[name last_name street city]
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'initializes the attributes' do
|
61
|
-
instance =
|
62
|
-
invariable.new(
|
63
|
-
name: 'John',
|
64
|
-
last_name: 'Doe',
|
65
|
-
street: '123 Main St',
|
66
|
-
city: 'Anytown'
|
67
|
-
)
|
68
|
-
|
69
|
-
expect(instance.name).to eq 'John'
|
70
|
-
expect(instance.last_name).to eq 'Doe'
|
71
|
-
expect(instance.street).to eq '123 Main St'
|
72
|
-
expect(instance.city).to eq 'Anytown'
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'initializes only given attributes' do
|
76
|
-
instance = invariable.new(last_name: 'Doe', city: 'Anytown')
|
77
|
-
expect(instance.name).to be_nil
|
78
|
-
expect(instance.last_name).to eq 'Doe'
|
79
|
-
expect(instance.street).to be_nil
|
80
|
-
expect(instance.city).to eq 'Anytown'
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'ignores unknown attributes' do
|
84
|
-
expect {
|
85
|
-
invariable.new(foo: 42, city: 'Anytown', ignored: true)
|
86
|
-
}.not_to raise_error
|
87
|
-
end
|
88
|
-
|
89
|
-
context 'when defining an already defined attribute of the superclass' do
|
90
|
-
it 'raises an exception' do
|
91
|
-
expect do
|
92
|
-
Class.new(base_class){ attribute :name }
|
93
|
-
end.to raise_error(NameError, 'attribute already defined - name')
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
data/spec/invariable_new_spec.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative 'helper'
|
3
|
-
|
4
|
-
RSpec.describe 'Invariable.new' do
|
5
|
-
context 'when only attribute names are given' do
|
6
|
-
subject(:invariable) { Invariable.new(:name, :last_name) }
|
7
|
-
|
8
|
-
it 'creates a new Class' do
|
9
|
-
expect(invariable).to be_a Class
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'is inherited from Object' do
|
13
|
-
expect(invariable).to be < Object
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'defines the attributes as instance methods' do
|
17
|
-
expect(invariable).to be_public_method_defined :name
|
18
|
-
expect(invariable).to be_public_method_defined :last_name
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
context 'when a base class and attribute names are given' do
|
23
|
-
subject(:invariable) { Invariable.new(foo_class, :name, :last_name) }
|
24
|
-
let(:foo_class) { Class.new }
|
25
|
-
|
26
|
-
it 'creates a new Class' do
|
27
|
-
expect(invariable).to be_a Class
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'is inherited from the given class' do
|
31
|
-
expect(invariable).to be < foo_class
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'defines the attributes as instance methods' do
|
35
|
-
expect(invariable).to be_public_method_defined :name
|
36
|
-
expect(invariable).to be_public_method_defined :last_name
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'when a block is given' do
|
41
|
-
subject(:invariable) do
|
42
|
-
Invariable.new(:name, :last_name) do
|
43
|
-
def full_name
|
44
|
-
"#{name} #{last_name}"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'allows to extend the new class' do
|
50
|
-
expect(invariable).to be_public_method_defined :full_name
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
data/spec/invariable_spec.rb
DELETED
@@ -1,291 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative 'helper'
|
3
|
-
|
4
|
-
RSpec.describe Invariable do
|
5
|
-
subject(:instance) do
|
6
|
-
sample_class.new(
|
7
|
-
name: 'John',
|
8
|
-
last_name: 'Doe',
|
9
|
-
address: {
|
10
|
-
zip: '45678',
|
11
|
-
city: 'Anytown',
|
12
|
-
street: '123 Main St'
|
13
|
-
}
|
14
|
-
)
|
15
|
-
end
|
16
|
-
|
17
|
-
let(:sample_class) do
|
18
|
-
Class.new do
|
19
|
-
include Invariable
|
20
|
-
attributes :name, :last_name
|
21
|
-
attribute address: Invariable.new(:city, :zip, :street)
|
22
|
-
|
23
|
-
def full_name
|
24
|
-
"#{name} #{last_name}"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'attributes' do
|
30
|
-
it 'allows to read the attributes by name' do
|
31
|
-
expect(instance.name).to eq 'John'
|
32
|
-
expect(instance.last_name).to eq 'Doe'
|
33
|
-
expect(instance.address).to be_a Invariable
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'provides information about its attributes' do
|
37
|
-
expect(instance.members).to eq %i[name last_name address]
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'can be checked whether an attribute is defined' do
|
41
|
-
expect(instance.member?(:last_name)).to be true
|
42
|
-
expect(instance.member?(:city)).to be false
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context 'Hash-like behavior' do
|
47
|
-
it 'provides Hash-like attribute access' do
|
48
|
-
expect(instance[:name]).to eq 'John'
|
49
|
-
expect(instance[:last_name]).to eq 'Doe'
|
50
|
-
expect(instance[:address][:city]).to eq 'Anytown'
|
51
|
-
end
|
52
|
-
|
53
|
-
context 'when the attribute name is unknown' do
|
54
|
-
it 'raises a NameError' do
|
55
|
-
expect { instance[:size_of_shoe] }.to raise_error(
|
56
|
-
NameError,
|
57
|
-
'not member - size_of_shoe'
|
58
|
-
)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'can be converted into a Hash' do
|
63
|
-
expect(instance.to_h).to eq(
|
64
|
-
name: 'John',
|
65
|
-
last_name: 'Doe',
|
66
|
-
address: {
|
67
|
-
zip: '45678',
|
68
|
-
city: 'Anytown',
|
69
|
-
street: '123 Main St'
|
70
|
-
}
|
71
|
-
)
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'can be converted into a customized Hash' do
|
75
|
-
converted = instance.to_h { |key, value| ["my_#{key}", value] }
|
76
|
-
expect(converted.keys).to eq %w[my_name my_last_name my_address]
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'allows to iterate all attribute name/value pairs' do
|
80
|
-
expect { |b| instance.each_pair(&b) }.to yield_successive_args(
|
81
|
-
[:name, 'John'],
|
82
|
-
[:last_name, 'Doe'],
|
83
|
-
[:address, instance.address]
|
84
|
-
)
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'provides an Enumerable for its attributes name/value pairs' do
|
88
|
-
expect(instance.each_pair).to be_a(Enumerable)
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'can be converted to a compact Hash' do
|
92
|
-
john = sample_class.new(name: 'John')
|
93
|
-
expect(john.to_h(compact: true)).to eq(name: 'John')
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context 'Array-like behavior' do
|
98
|
-
it 'provides its attribute count' do
|
99
|
-
expect(instance.size).to be 3
|
100
|
-
end
|
101
|
-
|
102
|
-
it 'provides Array-like attribute access' do
|
103
|
-
expect(instance[0]).to eq 'John'
|
104
|
-
expect(instance[1]).to eq 'Doe'
|
105
|
-
expect(instance[2]).to be instance.address
|
106
|
-
expect(instance[-1]).to be instance.address
|
107
|
-
end
|
108
|
-
|
109
|
-
context 'when the access index is out of bounds' do
|
110
|
-
it 'raises a NameError' do
|
111
|
-
expect { instance[3] }.to raise_error(IndexError, 'invalid offset - 3')
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'can be converted into an Array' do
|
116
|
-
expect(instance.to_a).to eq ['John', 'Doe', instance.address]
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'allows to iterate all attribute values' do
|
120
|
-
expect { |b| instance.each(&b) }.to yield_successive_args(
|
121
|
-
'John',
|
122
|
-
'Doe',
|
123
|
-
instance.address
|
124
|
-
)
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'provides an Enumerable for its attribute values' do
|
128
|
-
expect(instance.each).to be_a(Enumerable)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
context 'comparing' do
|
133
|
-
it 'can be compared to other objects' do
|
134
|
-
other =
|
135
|
-
sample_class.new(
|
136
|
-
name: 'John',
|
137
|
-
last_name: 'Doe',
|
138
|
-
address: {
|
139
|
-
zip: '45678',
|
140
|
-
city: 'Anytown',
|
141
|
-
street: '123 Main St'
|
142
|
-
}
|
143
|
-
)
|
144
|
-
expect(instance == other).to be true
|
145
|
-
|
146
|
-
other =
|
147
|
-
sample_class.new(
|
148
|
-
name: 'John',
|
149
|
-
last_name: 'Doe',
|
150
|
-
address: {
|
151
|
-
zip: '45678',
|
152
|
-
city: 'Anytown',
|
153
|
-
street: '124 Main St' # difffers
|
154
|
-
}
|
155
|
-
)
|
156
|
-
expect(instance == other).to be false
|
157
|
-
|
158
|
-
other =
|
159
|
-
double(
|
160
|
-
:other,
|
161
|
-
name: 'John',
|
162
|
-
last_name: 'Doe',
|
163
|
-
address:
|
164
|
-
double(
|
165
|
-
:other_addr,
|
166
|
-
zip: '45678',
|
167
|
-
city: 'Anytown',
|
168
|
-
street: '123 Main St'
|
169
|
-
)
|
170
|
-
)
|
171
|
-
expect(instance == other).to be true
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'can be tested for equality' do
|
175
|
-
other =
|
176
|
-
sample_class.new(
|
177
|
-
name: 'John',
|
178
|
-
last_name: 'Doe',
|
179
|
-
address: {
|
180
|
-
zip: '45678',
|
181
|
-
city: 'Anytown',
|
182
|
-
street: '123 Main St'
|
183
|
-
}
|
184
|
-
)
|
185
|
-
expect(instance.eql?(other)).to be true
|
186
|
-
|
187
|
-
other =
|
188
|
-
sample_class.new(
|
189
|
-
name: 'John',
|
190
|
-
last_name: 'Doe',
|
191
|
-
address: {
|
192
|
-
zip: '45679', # differs
|
193
|
-
city: 'Anytown',
|
194
|
-
street: '123 Main St'
|
195
|
-
}
|
196
|
-
)
|
197
|
-
expect(instance.eql?(other)).to be false
|
198
|
-
|
199
|
-
other = # class differs
|
200
|
-
double(
|
201
|
-
:other,
|
202
|
-
name: 'John',
|
203
|
-
last_name: 'Doe',
|
204
|
-
address: {
|
205
|
-
zip: '45678',
|
206
|
-
city: 'Anytown',
|
207
|
-
street: '123 Main St'
|
208
|
-
}
|
209
|
-
)
|
210
|
-
expect(instance.eql?(other)).to be false
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
context '#dig pattern' do
|
215
|
-
let(:data) { { person: instance } }
|
216
|
-
|
217
|
-
it 'can be used with attribute names' do
|
218
|
-
expect(data.dig(:person, :last_name)).to eq 'Doe'
|
219
|
-
expect(data.dig(:person, :zip)).to be_nil
|
220
|
-
expect(data.dig(:person, :address, :city)).to eq 'Anytown'
|
221
|
-
end
|
222
|
-
|
223
|
-
it 'can be used with indices' do
|
224
|
-
expect(data.dig(:person, 1)).to eq 'Doe'
|
225
|
-
expect(data.dig(:person, -1, :zip)).to eq '45678'
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
context 'pattern matching' do
|
230
|
-
it 'can be used for named pattern matching' do
|
231
|
-
result =
|
232
|
-
case instance
|
233
|
-
in name: 'Fred', last_name: 'Doe'
|
234
|
-
:fred
|
235
|
-
in name: 'John', last_name: 'New'
|
236
|
-
:not_john
|
237
|
-
in name: 'John', last_name: 'Doe', address: { city: 'NY' }
|
238
|
-
:john_from_ny
|
239
|
-
in name: 'John', last_name: 'Doe', address: { city: 'Anytown' }
|
240
|
-
:john
|
241
|
-
else
|
242
|
-
nil
|
243
|
-
end
|
244
|
-
|
245
|
-
expect(result).to be :john
|
246
|
-
end
|
247
|
-
|
248
|
-
it 'can be used for indexed pattern matching' do
|
249
|
-
result =
|
250
|
-
case instance
|
251
|
-
in 'Fred', 'Doe', *_
|
252
|
-
:fred
|
253
|
-
in 'John', 'New', *_
|
254
|
-
:not_john
|
255
|
-
in 'John', 'Doe', *_
|
256
|
-
:john
|
257
|
-
else
|
258
|
-
nil
|
259
|
-
end
|
260
|
-
|
261
|
-
expect(result).to be :john
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
it 'allows to create an updated version of itself' do
|
266
|
-
result =
|
267
|
-
instance.update(
|
268
|
-
name: 'Fred',
|
269
|
-
address: {
|
270
|
-
zip: '45678',
|
271
|
-
city: 'Anytown',
|
272
|
-
street: '124 Main St'
|
273
|
-
}
|
274
|
-
)
|
275
|
-
expect(result).to be_a sample_class
|
276
|
-
expect(result.name).to eq 'Fred'
|
277
|
-
expect(result.last_name).to eq 'Doe'
|
278
|
-
expect(result.address.to_h).to eq(
|
279
|
-
zip: '45678',
|
280
|
-
city: 'Anytown',
|
281
|
-
street: '124 Main St'
|
282
|
-
)
|
283
|
-
end
|
284
|
-
|
285
|
-
it 'can be inspected' do
|
286
|
-
expect(instance.inspect).to include(' name: "John", last_name: "Doe"')
|
287
|
-
expect(instance.inspect).to include(
|
288
|
-
'city: "Anytown", zip: "45678", street: "123 Main St"'
|
289
|
-
)
|
290
|
-
end
|
291
|
-
end
|