portrayal 0.2.0 → 0.3.1

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: eeb24e8ffff128d18963b1b9b35b542b260a2ec3b0e298064201c108a58c0c6c
4
- data.tar.gz: 36adc49da54f8a4756d08dd0f33fbff9b6b5366115082682ff9b9fd46bd26687
3
+ metadata.gz: 577a51dc874a79a5cb7aece4157c2d802005f3fa9c3daba4b807db522c7cd7ae
4
+ data.tar.gz: b30f1ea6a9438ba5b4574a68935602fce1dd0a447d15dce4227d0a67a10b2e87
5
5
  SHA512:
6
- metadata.gz: 992c3b99a75e3b0226d55cc6429072be5abac8248aab9e43903352a2aeb50b0ad29acdb3dd28993ae7ddabc46b3784440d5048afef958d1ffdae1bc9e6f4738c
7
- data.tar.gz: 4bc0a1cec3d59ae6db9e22e60504accc420e036ab197313e1c0b9f71219e3aae341acf82de1faa2f88984e6af79d19f1c4f825ad1fc5594b12db3497dcdc168c
6
+ metadata.gz: a02d197cb3c56e88afcda74a58d5d7034ef4739de707380c05b7a1a76c95e8ae1c8aa9341fde97d8efa69e6b5fcc00df429b2ada5e1b18f5dc9c2c51cc73504a
7
+ data.tar.gz: 4c6f5e4d359551e7420dd4331192f857f8a4518ab679b8052f36c6854a34a100a97bd4af98358589dcd08144cff9ddda071f6b2bd4602dc7495d2fc8467c6994
@@ -0,0 +1,24 @@
1
+ name: RSpec
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby: [ '2.4', '2.5', '2.6', '2.7' ]
15
+
16
+ name: Ruby ${{ matrix.ruby }}
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - uses: actions/setup-ruby@v1
20
+ with:
21
+ ruby-version: ${{ matrix.ruby }}
22
+ - run: gem install bundler
23
+ - run: bundle install
24
+ - run: bundle exec rake
data/CHANGELOG.md CHANGED
@@ -1,11 +1,23 @@
1
1
  This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
2
2
 
3
- ## [0.2.0] - 2019-07-03
3
+ ## Unreleased
4
4
 
5
- ### Changed
5
+ ## 0.3.1 - 2020-05-11
6
6
 
7
- * It's now possible to specify non-lambda default values, like `default: "foo". There is now also a distinction between a proc and a lambda default. Procs are `call`-ed, while lambdas or any other types are returned as-is. In the majority of cases defaults are static values, and there is no need for the performance overhead of making all defaults into anonymous functions.
7
+ * Fix the issue introduced in 0.3.0 where `==` and `eql?` were always treating rhs as another portrayal class.
8
8
 
9
- ## [0.1.0]
9
+ ## 0.3.0 - 2020-05-09
10
+
11
+ * No longer compare classes in `==`, use `eql?` for that. [[commit]](https://github.com/scottscheapflights/portrayal/commit/9c5a37e4fb91e35d23b22e208344452930452af7)
12
+ * Define a protected writer for every keyword - useful when applying changes after `dup`/`clone`. [[commit]](https://github.com/scottscheapflights/portrayal/commit/1c0fa6c6357a09760dae39165e864238d231a08e)
13
+ * Add definition of `#hash` to fix hash equality. Now `hash[object]` will match if `object` is of the same class with the same keywords and values. [[commit]](https://github.com/scottscheapflights/portrayal/commit/ba9e390ab4aea4733ba084ac273da448e313ea53)
14
+ * Make `#freeze` propagate to all keyword values. [[commit]](https://github.com/scottscheapflights/portrayal/commit/0a734411a6eac08e2355c4277e09a2a70800d032)
15
+ * Make `#dup` and `#clone` propagate to all keyword values. [[commit]](https://github.com/scottscheapflights/portrayal/commit/010632d87d81a8d5b5ea5ff27d3d209cc667b0a5)
16
+
17
+ ## 0.2.0 - 2019-07-03
18
+
19
+ * It's now possible to specify non-lambda default values, like `default: "foo"`. There is now also a distinction between a proc and a lambda default. Procs are `call`-ed, while lambdas or any other types are returned as-is. In the majority of cases defaults are static values, and there is no need for the performance overhead of making all defaults into anonymous functions. [[commit]](https://github.com/scottscheapflights/portrayal/commit/a1cc9d0fd40e413210f61b945d37b81c87280fee)
20
+
21
+ ## 0.1.0
10
22
 
11
23
  First version.
data/Gemfile.lock CHANGED
@@ -1,41 +1,41 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- portrayal (0.2.0)
4
+ portrayal (0.3.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  coderay (1.1.2)
10
10
  diff-lcs (1.3)
11
- method_source (0.9.2)
12
- pry (0.12.2)
13
- coderay (~> 1.1.0)
14
- method_source (~> 0.9.0)
15
- rake (10.5.0)
16
- rspec (3.8.0)
17
- rspec-core (~> 3.8.0)
18
- rspec-expectations (~> 3.8.0)
19
- rspec-mocks (~> 3.8.0)
20
- rspec-core (3.8.0)
21
- rspec-support (~> 3.8.0)
22
- rspec-expectations (3.8.3)
11
+ method_source (1.0.0)
12
+ pry (0.13.1)
13
+ coderay (~> 1.1)
14
+ method_source (~> 1.0)
15
+ rake (13.0.1)
16
+ rspec (3.9.0)
17
+ rspec-core (~> 3.9.0)
18
+ rspec-expectations (~> 3.9.0)
19
+ rspec-mocks (~> 3.9.0)
20
+ rspec-core (3.9.2)
21
+ rspec-support (~> 3.9.3)
22
+ rspec-expectations (3.9.2)
23
23
  diff-lcs (>= 1.2.0, < 2.0)
24
- rspec-support (~> 3.8.0)
25
- rspec-mocks (3.8.0)
24
+ rspec-support (~> 3.9.0)
25
+ rspec-mocks (3.9.1)
26
26
  diff-lcs (>= 1.2.0, < 2.0)
27
- rspec-support (~> 3.8.0)
28
- rspec-support (3.8.0)
27
+ rspec-support (~> 3.9.0)
28
+ rspec-support (3.9.3)
29
29
 
30
30
  PLATFORMS
31
31
  ruby
32
32
 
33
33
  DEPENDENCIES
34
- bundler (~> 2.0)
34
+ bundler (~> 2.1)
35
35
  portrayal!
36
- pry (~> 0.12)
37
- rake (~> 10.0)
38
- rspec (~> 3.0)
36
+ pry (~> 0.13)
37
+ rake (~> 13.0)
38
+ rspec (~> 3.9)
39
39
 
40
40
  BUNDLED WITH
41
- 2.0.1
41
+ 2.1.4
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ![RSpec](https://github.com/scottscheapflights/portrayal/workflows/RSpec/badge.svg?branch=master)
2
+
1
3
  # Portrayal
2
4
 
3
5
  Inspired by:
@@ -6,7 +8,7 @@ Inspired by:
6
8
  - Piotr Solnica's [virtus](https://github.com/solnic/virtus)
7
9
  - Everything [Michel Martens](https://github.com/soveran)
8
10
 
9
- Portrayal is a minimalist gem (~120 loc, no dependencies) for building struct-like classes. It provides a small yet powerful step up from plain ruby with its one and only `keyword` method.
11
+ Portrayal is a minimalist gem (~115 loc, no dependencies) for building struct-like classes. It provides a small yet powerful step up from plain ruby with its one and only `keyword` method.
10
12
 
11
13
  ```ruby
12
14
  class Person < MySuperClass
@@ -30,8 +32,12 @@ end
30
32
  When you call `keyword`:
31
33
 
32
34
  * It defines an `attr_reader`
35
+ * It defines a protected `attr_writer`
33
36
  * It defines `initialize`
34
37
  * It defines `==` and `eql?`
38
+ * It defines `#hash` for hash equality
39
+ * It defines `#dup` and `#clone` that propagate to all keyword values
40
+ * It defines `#freeze` that propagates to all keyword values
35
41
  * It creates a nested class when you supply a block
36
42
  * It inherits parent's superclass when creating a nested class
37
43
 
@@ -39,7 +45,8 @@ The code above produces almost exactly the following ruby. There's a lot of boil
39
45
 
40
46
  ```ruby
41
47
  class Person < MySuperClass
42
- attr_reader :name, :age, :favorite_fruit, :address
48
+ attr_accessor :name, :age, :favorite_fruit, :address
49
+ protected :name=, :age=, :favorite_fruit=, :address=
43
50
 
44
51
  def initialize(name:, age: nil, favorite_fruit: 'feijoa', address:)
45
52
  @name = name
@@ -48,16 +55,46 @@ class Person < MySuperClass
48
55
  @address = address
49
56
  end
50
57
 
58
+ def eql?(other)
59
+ self.class == other.class && self == other
60
+ end
61
+
51
62
  def ==(other)
52
- self.class == other.class &&
53
- { name: name, age: age, favorite_fruit: favorite_fruit, address: address } ==
63
+ { name: name, age: age, favorite_fruit: favorite_fruit, address: address } ==
54
64
  { name: other.name, age: other.age, favorite_fruit: other.favorite_fruit, address: other.address }
55
65
  end
56
66
 
57
- alias eql? ==
67
+ def hash
68
+ [ self.class, { name: name, age: age, favorite_fruit: favorite_fruit, address: address } ].hash
69
+ end
70
+
71
+ def freeze
72
+ name.freeze
73
+ age.freeze
74
+ favorite_fruit.freeze
75
+ address.freeze
76
+ super
77
+ end
78
+
79
+ def initialize_dup(source)
80
+ @name = source.name.dup
81
+ @age = source.age.dup
82
+ @favorite_fruit = source.favorite_fruit.dup
83
+ @address = source.address.dup
84
+ super
85
+ end
86
+
87
+ def initialize_clone(source)
88
+ @name = source.name.clone
89
+ @age = source.age.clone
90
+ @favorite_fruit = source.favorite_fruit.clone
91
+ @address = source.address.clone
92
+ super
93
+ end
58
94
 
59
95
  class Address < MySuperClass
60
- attr_reader :street, :city
96
+ attr_accessor :street, :city
97
+ protected :street=, :city=
61
98
 
62
99
  def initialize(street:, city:)
63
100
  @street = street
@@ -68,12 +105,35 @@ class Person < MySuperClass
68
105
  "#{street}, #{city}"
69
106
  end
70
107
 
108
+ def eql?(other)
109
+ self.class == other.class && self == other
110
+ end
111
+
71
112
  def ==(other)
72
- self.class == other.class &&
73
- { street: street, city: city } == { street: other.street, city: other.city }
113
+ { street: street, city: city } == { street: other.street, city: other.city }
74
114
  end
75
115
 
76
- alias eql? ==
116
+ def hash
117
+ [ self.class, { street: street, city: city } ].hash
118
+ end
119
+
120
+ def freeze
121
+ street.freeze
122
+ city.freeze
123
+ super
124
+ end
125
+
126
+ def initialize_dup(source)
127
+ @street = source.street.dup
128
+ @city = source.city.dup
129
+ super
130
+ end
131
+
132
+ def initialize_clone(source)
133
+ @street = source.street.clone
134
+ @city = source.city.clone
135
+ super
136
+ end
77
137
  end
78
138
  end
79
139
  ```
@@ -7,30 +7,20 @@ module Portrayal
7
7
  @equality_defined = false
8
8
  end
9
9
 
10
- def [](name)
11
- @schema[name]
12
- end
13
-
14
- def keywords
15
- @schema.keys
16
- end
10
+ def keywords; @schema.keys end
11
+ def [](name); @schema[name] end
17
12
 
18
13
  def attributes(object)
19
- Hash[
20
- object.class.portrayal.keywords.map { |key| [key, object.send(key)] }
21
- ]
14
+ Hash[object.class.portrayal.keywords.map { |k| [k, object.send(k)] }]
22
15
  end
23
16
 
24
17
  def add_keyword(name, optional, default)
25
18
  optional, default =
26
- if optional == NULL && default == NULL
27
- [false, nil]
28
- elsif optional != NULL && default == NULL
29
- [optional, optional ? [:return, nil] : nil]
30
- elsif optional == NULL && default != NULL
31
- [true, [default_strategy(default), default]]
32
- else
33
- [optional, optional ? [default_strategy(default), default] : nil]
19
+ case [optional == NULL, default == NULL]
20
+ when [true, true]; [false, nil]
21
+ when [false, true]; [optional, optional ? [:return, nil] : nil]
22
+ when [true, false]; [true, [default_strategy(default), default]]
23
+ else; [optional, optional ? [default_strategy(default), default] : nil]
34
24
  end
35
25
 
36
26
  @schema[name.to_sym] = { optional: optional, default: default }
@@ -50,44 +40,46 @@ module Portrayal
50
40
  end
51
41
 
52
42
  def definition_of_initialize
53
- init_args =
54
- @schema
55
- .map { |name, config|
56
- if config[:optional]
57
- "#{name}: self.class.portrayal.get_default(:#{name})"
58
- else
59
- "#{name}:"
60
- end
61
- }
62
- .join(',')
63
-
64
- init_assignments =
65
- @schema
66
- .keys
67
- .map { |name| "@#{name} = #{name}" }
68
- .join('; ')
69
-
70
- "def initialize(#{init_args}); #{init_assignments} end"
43
+ init_args = @schema.map { |name, config|
44
+ config[:optional] ?
45
+ "#{name}: self.class.portrayal.get_default(:#{name})" : "#{name}:"
46
+ }.join(',')
47
+
48
+ init_assigns = @schema.keys.map { |name| "@#{name} = #{name}" }.join('; ')
49
+ "def initialize(#{init_args}); #{init_assigns} end"
71
50
  end
72
51
 
73
- def definition_of_equality
52
+ def definition_of_object_enhancements
74
53
  <<-RUBY
54
+ def eql?(other); self.class == other.class && self == other end
55
+ def hash; [self.class, self.class.portrayal.attributes(self)].hash end
56
+
75
57
  def ==(other)
76
- self.class == other.class &&
77
- self.class.portrayal.attributes(self) ==
58
+ return super unless other.class.is_a?(Portrayal)
59
+
60
+ self.class.portrayal.attributes(self) ==
78
61
  self.class.portrayal.attributes(other)
79
62
  end
80
63
 
81
- alias eql? ==
82
- RUBY
83
- end
64
+ def freeze
65
+ self.class.portrayal.attributes(self).values.each(&:freeze)
66
+ super
67
+ end
84
68
 
85
- def equality_defined?
86
- @equality_defined
87
- end
69
+ def initialize_dup(source)
70
+ self.class.portrayal.attributes(source).each do |key, value|
71
+ instance_variable_set('@' + key.to_s, value.dup)
72
+ end
73
+ super
74
+ end
88
75
 
89
- def mark_equality_defined
90
- @equality_defined = true
76
+ def initialize_clone(source)
77
+ self.class.portrayal.attributes(source).each do |key, value|
78
+ instance_variable_set('@' + key.to_s, value.clone)
79
+ end
80
+ super
81
+ end
82
+ RUBY
91
83
  end
92
84
  end
93
85
  end
@@ -1,3 +1,3 @@
1
1
  module Portrayal
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.1"
3
3
  end
data/lib/portrayal.rb CHANGED
@@ -8,18 +8,15 @@ module Portrayal
8
8
  unless respond_to?(:portrayal)
9
9
  class << self; attr_reader :portrayal end
10
10
  @portrayal = Schema.new
11
+ class_eval(portrayal.definition_of_object_enhancements)
11
12
  end
12
13
 
13
- attr_reader name
14
+ attr_accessor name
15
+ protected "#{name}="
14
16
 
15
17
  portrayal.add_keyword(name, optional, default)
16
18
  class_eval(portrayal.definition_of_initialize)
17
19
 
18
- unless portrayal.equality_defined?
19
- class_eval(portrayal.definition_of_equality)
20
- portrayal.mark_equality_defined
21
- end
22
-
23
20
  if block_given?
24
21
  keyword_class = Class.new(superclass) { extend Portrayal }
25
22
  keyword_class.class_eval(&block)
data/portrayal.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.email = ['madfancier@gmail.com']
10
10
 
11
11
  spec.summary = 'A minimal builder for struct-like classes'
12
- spec.description = 'Inspired by dry-initializer and virtus, portrayal is a minimalist gem (~120 loc, no dependencies) that takes a somewhat different approach to building struct-like classes. It steps away from types, coersion, and writer methods in favor of encouraging well-designed constructors. Read more in the Philosophy section of the README.'
12
+ spec.description = 'Inspired by dry-initializer and virtus, portrayal is a minimalist gem that takes a somewhat different approach to building struct-like classes. It steps away from types, coersion, and writer methods in favor of encouraging well-designed constructors. Read more in the Philosophy section of the README.'
13
13
  spec.homepage = 'https://github.com/scottscheapflights/portrayal'
14
14
  spec.license = 'Apache-2.0'
15
15
 
@@ -18,8 +18,8 @@ Gem::Specification.new do |spec|
18
18
  end
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency 'bundler', '~> 2.0'
22
- spec.add_development_dependency 'rake', '~> 10.0'
23
- spec.add_development_dependency 'rspec', '~> 3.0'
24
- spec.add_development_dependency 'pry', '~> 0.12'
21
+ spec.add_development_dependency 'bundler', '~> 2.1'
22
+ spec.add_development_dependency 'rake', '~> 13.0'
23
+ spec.add_development_dependency 'rspec', '~> 3.9'
24
+ spec.add_development_dependency 'pry', '~> 0.13'
25
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: portrayal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxim Chernyak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-03 00:00:00.000000000 Z
11
+ date: 2020-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,70 +16,69 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '2.1'
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: '2.0'
26
+ version: '2.1'
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: '10.0'
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: '10.0'
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.0'
47
+ version: '3.9'
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.0'
54
+ version: '3.9'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.12'
61
+ version: '0.13'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.12'
68
+ version: '0.13'
69
69
  description: Inspired by dry-initializer and virtus, portrayal is a minimalist gem
70
- (~120 loc, no dependencies) that takes a somewhat different approach to building
71
- struct-like classes. It steps away from types, coersion, and writer methods in favor
72
- of encouraging well-designed constructors. Read more in the Philosophy section of
73
- the README.
70
+ that takes a somewhat different approach to building struct-like classes. It steps
71
+ away from types, coersion, and writer methods in favor of encouraging well-designed
72
+ constructors. Read more in the Philosophy section of the README.
74
73
  email:
75
74
  - madfancier@gmail.com
76
75
  executables: []
77
76
  extensions: []
78
77
  extra_rdoc_files: []
79
78
  files:
79
+ - ".github/workflows/rspec.yml"
80
80
  - ".gitignore"
81
81
  - ".rspec"
82
- - ".travis.yml"
83
82
  - CHANGELOG.md
84
83
  - CODE_OF_CONDUCT.md
85
84
  - Gemfile
@@ -112,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
111
  - !ruby/object:Gem::Version
113
112
  version: '0'
114
113
  requirements: []
115
- rubygems_version: 3.0.3
114
+ rubygems_version: 3.1.2
116
115
  signing_key:
117
116
  specification_version: 4
118
117
  summary: A minimal builder for struct-like classes
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 2.3
7
- - 2.4
8
- - 2.5
9
- - 2.6
10
- before_install: gem install bundler -v 2.0.1