ivo 0.4.0 → 0.5.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
- SHA1:
3
- metadata.gz: 3877d0d14eca70586641e288f3067cfb201764a8
4
- data.tar.gz: ccc3b46313c22dbc1739dc17c862b24b4a79e44f
2
+ SHA256:
3
+ metadata.gz: 6f7f83b9399c97a63549923922baf6d8d82898ba7826050fd8186cbe81f9b8b6
4
+ data.tar.gz: 42e2ec90299185b2e4a0769db49225d5fad0ab17e358ec512f24df60e14e8d92
5
5
  SHA512:
6
- metadata.gz: 17037ae262928c28f4314a1ef9232acbe0ba6dad33fa7939ec613fa1c60941e3f1f6f2ac118a140cb21cff2572c6aa900320f19f86b8c99d9dbf72b726ec782b
7
- data.tar.gz: 19088a2316bbd097bcabe3fb900d64f4960a4a21b9941f85b45c32069ac0158b4c6013e88b14512bd6b91d93f4de236a57ba50cb6c45842ed710537806e3d1a1
6
+ metadata.gz: e058a43abd113a379f565504059d594cbe6688d42edef8ecf6746ff9553b27a8e20cd09b431c25235a1899368f6aa42a6674127ba58c00c30aa8832d18b147e8
7
+ data.tar.gz: 4b1d014d31aabfa339f8b4d0a1659a6855af5b8242601c1a8c67f265b891b04cac7a05711e5ec592bfca3d70840d7c4e503d27b39db4038cc89e18b2b88c9d97
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Allen Madsen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Ivo
2
2
 
3
+ A library for creating immutable value objects.
4
+
3
5
  ## Installation
4
6
 
5
7
  Add this line to your application's Gemfile:
@@ -18,7 +20,87 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
21
- Coming soon.
23
+ ### Basics
24
+
25
+ ```ruby
26
+ Car = Ivo.new(:make, :model)
27
+
28
+ car = Car.new('Honda', 'Accord')
29
+
30
+ car.make # "Honda"
31
+ car.model # "Accord"
32
+ car.frozen? # true
33
+ ```
34
+
35
+ Objects can also be instantiated with keywords:
36
+
37
+ ```ruby
38
+ car = Car.with(make: 'Honda', model: 'Accord')
39
+ ```
40
+
41
+ `Ivo.new` also accepts a block for additional definitions:
42
+
43
+ ```ruby
44
+ Car = Ivo.new(:make, :model) do
45
+ def to_s
46
+ "#{make} #{model}"
47
+ end
48
+ end
49
+ ```
50
+
51
+ For one-off value objects:
52
+
53
+ ```ruby
54
+ car = Ivo.(make: 'Honda', model: 'Accord')
55
+
56
+ car.make # "Honda"
57
+ car.model # "Accord"
58
+ car.frozen? # true
59
+ ```
60
+
61
+ ### Under the hood
62
+
63
+ This:
64
+
65
+ ```ruby
66
+ Car = Ivo.new(:make, :model) do
67
+ def to_s
68
+ "#{make} #{model}"
69
+ end
70
+ end
71
+ ```
72
+
73
+ is equivalent to:
74
+
75
+ ```ruby
76
+ class Car
77
+ def self.with(make: nil, model: nil)
78
+ new(make, model)
79
+ end
80
+
81
+ def initialize(make = nil, model = nil)
82
+ @make = make
83
+ @model = model
84
+ freeze
85
+ end
86
+
87
+ attr_reader :make, :model
88
+
89
+ def to_s
90
+ "#{make} #{model}"
91
+ end
92
+
93
+ def ==(other)
94
+ self.class == other.class && make == other.make && model == other.model
95
+ end
96
+
97
+ alias_method :eql?, :==
98
+
99
+ def hash
100
+ make.hash ^ model.hash
101
+ end
102
+ end
103
+ ```
22
104
 
23
105
  ## Development
24
106
 
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/inline'
4
+
5
+ require 'benchmark'
6
+ require 'ostruct'
7
+
8
+ gemfile do
9
+ source 'https://rubygems.org'
10
+
11
+ gem 'ivo', '~> 0.4'
12
+ gem 'immutable-struct', '~> 2.4'
13
+ gem 'values', '~> 1.8'
14
+ end
15
+
16
+ StructClass = Struct.new(:x, :y)
17
+ ImmutableStructClass = ImmutableStruct.new(:x, :y)
18
+ ValueClass = Value.new(:x, :y)
19
+ IvoClass = Ivo.new(:x, :y)
20
+
21
+ n = 1_000_000
22
+
23
+ Benchmark.bm(30) do |x|
24
+ x.report('StructClass.new') do
25
+ n.times do
26
+ StructClass.new(1, 2)
27
+ end
28
+ end
29
+
30
+ x.report('IvoClass.new') do
31
+ n.times do
32
+ IvoClass.new(1, 2)
33
+ end
34
+ end
35
+
36
+ x.report('IvoClass.with') do
37
+ n.times do
38
+ IvoClass.with(x: 1, y: 2)
39
+ end
40
+ end
41
+
42
+ x.report('ValueClass.new') do
43
+ n.times do
44
+ ValueClass.new(1, 2)
45
+ end
46
+ end
47
+
48
+ x.report('ValueClass.with') do
49
+ n.times do
50
+ ValueClass.with(x: 1, y: 2)
51
+ end
52
+ end
53
+
54
+ x.report('ImmutableStructClass.new') do
55
+ n.times do
56
+ ImmutableStructClass.new(x: 1, y: 2)
57
+ end
58
+ end
59
+ end
60
+
61
+ Benchmark.bm(30) do |x|
62
+ x.report('OpenStruct.new') do
63
+ n.times do
64
+ OpenStruct.new(x: 1, y: 2)
65
+ end
66
+ end
67
+
68
+ x.report('Ivo.call') do
69
+ n.times do
70
+ Ivo.call(x: 1, y: 2)
71
+ end
72
+ end
73
+ end
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_development_dependency "bundler", "~> 1"
22
+ spec.add_development_dependency "bundler", "~> 2"
23
23
  spec.add_development_dependency "rake", "~> 12"
24
24
  spec.add_development_dependency "rspec", "~> 3"
25
25
  end
data/lib/ivo.rb CHANGED
@@ -1,71 +1,121 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ivo/version"
2
4
  require 'ivo/value'
3
5
 
4
6
  module Ivo
5
- def self.new(*attrs, &block)
6
- Class.new do
7
- include Ivo.value *attrs
7
+ extend self
8
+
9
+ def new(*attrs, &block)
10
+ instance_ruby = build_instance_ruby(attrs)
11
+ class_ruby = build_class_ruby(attrs)
8
12
 
9
- class_eval &block if block
13
+ Class.new do
14
+ instance_eval(instance_ruby)
15
+ class_eval(class_ruby)
16
+ class_eval(&block) if block
10
17
  end
11
18
  end
12
19
 
13
- def self.value(*attrs)
14
- # a: nil, b: nil
15
- keyword_args = attrs.map { |attr| "#{attr}: nil" }.join ', '
20
+ def value(*attrs)
21
+ instance_ruby = build_instance_ruby(attrs)
22
+ class_ruby = build_class_ruby(attrs)
16
23
 
17
- # a, b
18
- args = attrs.map { |attr| "#{attr} = nil" }.join ', '
24
+ template = <<~RUBY
25
+ module ClassMethods
26
+ %{instance_ruby}
27
+ end
19
28
 
20
- # @a = a
21
- # @b = b
22
- instance_variable_assignments = attrs.map { |attr| "@#{attr} = #{attr}" }.join "\n"
29
+ def self.included(klass)
30
+ klass.extend(ClassMethods)
31
+ end
32
+ RUBY
23
33
 
24
- equality_check = begin
25
- checks = ['self.class == other.class']
26
- attrs.each { |attr| checks << "#{attr} == other.#{attr}" }
27
- checks.join ' && '
28
- end
34
+ ruby = template % {instance_ruby: instance_ruby}
29
35
 
30
- hash = attrs.map { |attr| "#{attr}.hash" }.join ' ^ '
36
+ Module.new do
37
+ module_eval(ruby)
38
+ module_eval(class_ruby)
39
+ end
40
+ end
31
41
 
32
- code = <<~RUBY
33
- def self.included(base)
34
- base.extend ClassMethods
35
- end
42
+ def call(attrs = nil)
43
+ Value.new(attrs)
44
+ end
36
45
 
37
- module ClassMethods
38
- def with(#{keyword_args})
39
- new #{attrs.join ', '}
40
- end
41
- end
46
+ private
42
47
 
43
- def initialize(#{args})
44
- #{instance_variable_assignments}
48
+ def build_class_ruby(attrs)
49
+ template = <<~RUBY
50
+ def initialize(%{args})
51
+ %{instance_variable_assignments}
45
52
  freeze
46
53
  end
47
54
 
55
+ %{attr_reader_declaration}
56
+
48
57
  def ==(other)
49
- #{equality_check}
58
+ %{equality_check}
50
59
  end
51
60
 
52
- def hash
53
- #{hash}
54
- end
61
+ alias_method :eql?, :==
62
+
63
+ %{hash_method}
55
64
  RUBY
56
65
 
57
- Module.new do
58
- module_eval code
66
+ # a, b
67
+ args = attrs.map { |attr| "#{attr} = nil" }.join(', ')
59
68
 
60
- attr_reader *attrs
69
+ # @a = a
70
+ # @b = b
71
+ instance_variable_assignments = attrs.map { |attr| "@#{attr} = #{attr}" }.join("\n")
61
72
 
62
- def eql?(other)
63
- self == other
64
- end
73
+ # attr_reader :a, :b
74
+ if attrs.any?
75
+ arg_symbols = attrs.map { |attr| ":#{attr}" }.join(', ')
76
+ attr_reader_declaration = "attr_reader #{arg_symbols}"
65
77
  end
78
+
79
+ # self.class == other.class && a == other.a && b == other.b
80
+ equality_check = begin
81
+ checks = ['self.class == other.class']
82
+ attrs.each { |attr| checks << "#{attr} == other.#{attr}" }
83
+ checks.join(' && ')
84
+ end
85
+
86
+ # def hash
87
+ # a.hash ^ b.hash
88
+ # end
89
+ if attrs.any?
90
+ hash = attrs.map { |attr| "#{attr}.hash" }.join(' ^ ')
91
+ hash_method = <<~RUBY
92
+ def hash
93
+ #{hash}
94
+ end
95
+ RUBY
96
+ end
97
+
98
+ template % {
99
+ args: args,
100
+ instance_variable_assignments: instance_variable_assignments,
101
+ attr_reader_declaration: attr_reader_declaration,
102
+ equality_check: equality_check,
103
+ hash_method: hash_method,
104
+ }
66
105
  end
67
106
 
68
- def self.call(attrs = nil)
69
- Value.new attrs
107
+ def build_instance_ruby(attrs)
108
+ template = <<~RUBY
109
+ def with(%{keyword_args})
110
+ new(%{values})
111
+ end
112
+ RUBY
113
+
114
+ # a: nil, b: nil
115
+ keyword_args = attrs.map { |attr| "#{attr}: nil" }.join(', ')
116
+
117
+ values = attrs.join(', ')
118
+
119
+ template % {keyword_args: keyword_args, values: values}
70
120
  end
71
121
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Ivo
2
4
  class Value
3
5
  def initialize(data = nil)
@@ -25,7 +27,7 @@ module Ivo
25
27
  end
26
28
 
27
29
  def inspect
28
- attributes = data.map { |key, value| "#{key}=#{value.inspect}" }.join ', '
30
+ attributes = data.map { |key, value| "#{key}=#{value.inspect}" }.join(', ')
29
31
  "#<#{self.class.name} #{attributes}>"
30
32
  end
31
33
 
@@ -1,3 +1,3 @@
1
1
  module Ivo
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ivo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Austin Schneider
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-05-11 00:00:00.000000000 Z
11
+ date: 2019-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1'
19
+ version: '2'
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'
26
+ version: '2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -62,8 +62,10 @@ files:
62
62
  - ".gitignore"
63
63
  - ".rspec"
64
64
  - Gemfile
65
+ - LICENSE.txt
65
66
  - README.md
66
67
  - Rakefile
68
+ - benchmarks.rb
67
69
  - bin/console
68
70
  - bin/setup
69
71
  - ivo.gemspec
@@ -88,8 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
90
  - !ruby/object:Gem::Version
89
91
  version: '0'
90
92
  requirements: []
91
- rubyforge_project:
92
- rubygems_version: 2.6.11
93
+ rubygems_version: 3.0.3
93
94
  signing_key:
94
95
  specification_version: 4
95
96
  summary: Library for creating immutable value objects (IVO)