data_class_factory 0.0.beta0 → 0.1.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: 13db8d588b7024e13dd87db5fade13f9b00c87a6ba480d43361ea51dc9494296
4
- data.tar.gz: 3b1f1c66790b23e8fd87557afb30979655af70687fff44c459707716ba25f5de
3
+ metadata.gz: 85a61ac673bb16b61d606b2a332aada35a31ae1e62d0db7140fbe88f1598d84a
4
+ data.tar.gz: c0533d7643ef9eaca60995f7d35797179f55471f883fb15c943e9ef53a1d95a6
5
5
  SHA512:
6
- metadata.gz: a84caf0dcadf17210bdd61a4ea284c1b2d54a1c4231ae2a72feca44a654ea52f4e8cdc0d6af64981d6f2b10cbfca1938487ff7bb2f934a3f6156d2253ac9d406
7
- data.tar.gz: 06c62651ce5e6de2c63666b084f18010d5b1ce5bcdd033674bb88cf5960d67f101f0d5e00b376b9d54b5584e5592225812805ea56d70edfbc5ad189b54e2b0a9
6
+ metadata.gz: bcb368e9071a14ba6da85043a57d46897ab9ee016e9587ca674964c8a39858a6be0e476f47db94718db761efd4896e0b3db156eca1457ca6294e5b8fdb535b22
7
+ data.tar.gz: 1f5ca0c39dbd680fd81b6587fce069a05f5653f6b898e806a22a7ca3831572596f1218dff47bd898994492ae447b38e8fc58dcdb134b8c73e0d8b457948c2ed9
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in data_class_factory.gemspec
6
8
  gemspec
data/README.md CHANGED
@@ -1,38 +1,67 @@
1
- # DataClassFactory
1
+ [![Gem Version](https://badge.fury.io/rb/data_class_factory.svg)](https://badge.fury.io/rb/data_class_factory)
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/data_class_factory`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ # Data class factory
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Backport `Data.define` for Ruby 2.7, 3.0, 3.1 :)
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your application's Gemfile:
10
-
11
9
  ```ruby
12
10
  gem 'data_class_factory'
13
11
  ```
14
12
 
15
- And then execute:
13
+ and then `bundle install`
16
14
 
17
- $ bundle
15
+ ## Usage
18
16
 
19
- Or install it yourself as:
17
+ ```ruby
18
+ require 'data_class_factory'
19
+
20
+ Point = Data.define(:x, :y) do
21
+ def norm
22
+ Math.sqrt(x * x + y * y)
23
+ end
24
+ end
25
+ p1 = Point.new(x: 3, y: 4)
26
+ p2 = Point.new(x: 3, y: 4)
27
+ p1 == p2 # => true
28
+ ```
20
29
 
21
- $ gem install data_class_factory
30
+ Most features of the data class are ported. See https://docs.ruby-lang.org/en/3.2/Data.html for checking the original spec of `Data`.
22
31
 
23
- ## Usage
32
+ One difference is that backported Data class intentionally accepts only keyword arguments for `.new` while original Data class accepts both positional args and keyword args.
24
33
 
25
- TODO: Write usage instructions here
34
+ ```ruby
35
+ Point = Data.define(:x, :y) do
36
+ def norm
37
+ Math.sqrt(x * x + y * y)
38
+ end
39
+ end
26
40
 
27
- ## Development
41
+ # raises ArgumentError
42
+ # wrong number of arguments (given 2, expected 0)
43
+ p1 = Point.new(3, 4)
28
44
 
29
- 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.
45
+ p1 = Point.new(x: 3, y: 4) # works well :)
46
+ ```
30
47
 
31
- 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).
48
+ ### aliasing
32
49
 
33
- ## Contributing
50
+ REMARK this gem removes `Data` [defined on Ruby 2.7](https://ruby-doc.org/core-2.7.0/Data.html), and respects original `Data` on Ruby >= 3.2. If you prefer using this gem also on Ruby >= 3.2, choose another name like this.
34
51
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/data_class_factory. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
52
+ ```ruby
53
+ MyDataClass = DataClassFactory.define_factory_class
54
+ ```
55
+
56
+ We can use the aliased class as usual like below.
57
+
58
+ ```ruby
59
+ Point = MyDataClass.define(:x, :y) do
60
+ def norm
61
+ Math.sqrt(x * x + y * y)
62
+ end
63
+ end
64
+ ```
36
65
 
37
66
  ## License
38
67
 
@@ -40,4 +69,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
40
69
 
41
70
  ## Code of Conduct
42
71
 
43
- Everyone interacting in the DataClassFactory project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/data_class_factory/blob/master/CODE_OF_CONDUCT.md).
72
+ Everyone interacting in the DataClassFactory project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](./CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,8 +1,10 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
3
5
 
4
6
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
8
10
  end
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "data_class_factory"
4
+ require 'bundler/setup'
5
+ require 'data_class_factory'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "data_class_factory"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
@@ -1,16 +1,17 @@
1
+ # frozen_string_literal: true
1
2
 
2
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "data_class_factory/version"
5
+ require 'data_class_factory/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "data_class_factory"
8
+ spec.name = 'data_class_factory'
8
9
  spec.version = DataClassFactory::VERSION
9
- spec.authors = ["YusukeIwaki"]
10
+ spec.authors = ['YusukeIwaki']
10
11
 
11
- spec.summary = "Backport `Data.define` for Ruby < 3.2"
12
- spec.homepage = "https://github.com/YusukeIwaki/data_class_factory"
13
- spec.license = "MIT"
12
+ spec.summary = 'Backport `Data.define` for Ruby 2.7, 3.0, 3.1'
13
+ spec.homepage = 'https://github.com/YusukeIwaki/data_class_factory'
14
+ spec.license = 'MIT'
14
15
 
15
16
  # Specify which files should be added to the gem when it is released.
16
17
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -19,12 +20,15 @@ Gem::Specification.new do |spec|
19
20
  f.match(%r{^(test|spec|features)/}) || f.include?('.git')
20
21
  end
21
22
  end
22
- spec.bindir = "exe"
23
+ spec.bindir = 'exe'
23
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
- spec.require_paths = ["lib"]
25
+ spec.require_paths = ['lib']
25
26
 
26
27
  spec.required_ruby_version = '>= 2.7'
27
- spec.add_development_dependency "bundler"
28
- spec.add_development_dependency "rake"
29
- spec.add_development_dependency "minitest"
28
+ spec.add_development_dependency 'bundler'
29
+ spec.add_development_dependency 'minitest'
30
+ spec.add_development_dependency 'pry'
31
+ spec.add_development_dependency 'rake'
32
+ spec.add_development_dependency 'rubocop'
33
+ spec.add_development_dependency 'rubocop-minitest'
30
34
  end
data/lib/data.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0.0')
4
+ warn 'Removing original `Data` class', uplevel: 3
5
+ Object.send(:remove_const, :Data)
6
+ end
7
+ Data = DataClassFactory.define_factory_class if DataClassFactory.backport?
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module DataClass
6
+ # An internal class for providing validation of `Data.define` and its initializer.
7
+ class Definition
8
+ # @param attribute_names [Array<Symbol>]
9
+ def initialize(attribute_names)
10
+ validate_attribute_names(attribute_names)
11
+ @attribute_names = attribute_names.each { |key| validate_attribute_name(key) }
12
+ end
13
+ attr_reader :attribute_names
14
+
15
+ # @param kwargs [Hash<Symbol, Object>]
16
+ def validate(kwargs)
17
+ if attribute_names - kwargs.keys != []
18
+ raise ArgumentError, "missing keyword: #{(attribute_names - kwargs.keys).join(', ')}"
19
+ end
20
+
21
+ if kwargs.keys - attribute_names != []
22
+ raise ArgumentError, "unknown keyword: #{(kwargs.keys - attribute_names).join(', ')}"
23
+ end
24
+
25
+ nil
26
+ end
27
+
28
+ private
29
+
30
+ def validate_attribute_names(attribute_names)
31
+ checked = Set.new
32
+ attribute_names.each do |key|
33
+ raise TypeError, "#{key} is not a symbol" unless key.is_a?(Symbol)
34
+ raise ArgumentError, "invalid data member: #{key}" if key.end_with?('=')
35
+ raise ArgumentError, "duplicate member: #{key}" if checked.include?(key)
36
+
37
+ checked << key
38
+ end
39
+ end
40
+
41
+ def validate_attribute_name(key); end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataClass
4
+ # An internal class for providing implementation of `Data.define`.
5
+ class Factory
6
+ # @param attribute_names [Array<Symbol>]
7
+ def initialize(attribute_names)
8
+ @definition = Definition.new(attribute_names)
9
+ end
10
+
11
+ # @param parent_class [Data]
12
+ # @return [Class<Data>]
13
+ def create(parent_class:, &block)
14
+ attribute_names = @definition.attribute_names
15
+
16
+ # defines a subclass of Data.
17
+ Class.new(parent_class) do
18
+ public_class_method :new
19
+ private_class_method :define
20
+
21
+ attribute_names.each { |key| define_method(key) { @data[key] } }
22
+
23
+ define_singleton_method(:members) { attribute_names }
24
+
25
+ class_eval(&block) unless block.nil?
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataClass
4
+ # An internal module for providing instance methods for `Data.define`.
5
+ module InstanceMethods
6
+ def initialize(**kwargs)
7
+ definition = DataClass::Definition.new(self.class.members)
8
+ definition.validate(kwargs)
9
+
10
+ @data = kwargs.each_with_object({}) do |entry, h|
11
+ key, value = entry
12
+ h[key] = value
13
+ end.freeze
14
+ freeze
15
+ end
16
+
17
+ def initialize_copy(other)
18
+ @data = other.instance_variable_get(:@data).dup
19
+ @data.freeze
20
+ freeze
21
+ end
22
+
23
+ def deconstruct
24
+ @data.values
25
+ end
26
+
27
+ # @param array_of_names_or_nil [Array<Symbol>, nil]
28
+ def deconstruct_keys(array_of_names_or_nil)
29
+ return to_h if array_of_names_or_nil.nil?
30
+
31
+ unless array_of_names_or_nil.is_a?(Enumerable)
32
+ raise TypeError, "wrong argument type #{array_of_names_or_nil.class} (expected Array or nil)"
33
+ end
34
+
35
+ array_of_names_or_nil.each_with_object({}) do |key, h|
36
+ h[key] = @data[key] if @data[key]
37
+ end
38
+ end
39
+
40
+ # @return [Boolean]
41
+ def eql?(other)
42
+ hash_for_comparation.eql?(other.hash_for_comparation)
43
+ end
44
+
45
+ # @return [Integer]
46
+ def hash
47
+ hash_for_comparation.hash
48
+ end
49
+
50
+ # @return [String]
51
+ def inspect
52
+ if self.class.name
53
+ "#<data #{self.class.name} #{inspect_members}>"
54
+ else
55
+ "#<data #{inspect_members}>"
56
+ end
57
+ end
58
+
59
+ # @return [Hash]
60
+ def marshal_dump
61
+ @data
62
+ end
63
+
64
+ # @param dump [Hash]
65
+ def marshal_load(dump)
66
+ raise TypeError, 'dump must be a Hash' unless dump.is_a?(Hash)
67
+
68
+ initialize(**dump)
69
+ end
70
+
71
+ def members
72
+ self.class.members
73
+ end
74
+
75
+ def to_h(&block)
76
+ @data.each_with_object({}) do |key_and_value, h|
77
+ key, value = block ? block.call(*key_and_value) : key_and_value
78
+ h[key] = value
79
+ end.to_h
80
+ end
81
+
82
+ def to_s
83
+ inspect
84
+ end
85
+
86
+ def with(**kwargs)
87
+ return self if kwargs.empty?
88
+
89
+ unknown_keywords = kwargs.keys - @data.keys
90
+ raise ArgumentError, "unknown keywords: #{unknown_keywords.join(', ')}" unless unknown_keywords.empty?
91
+
92
+ new_data = @data.merge(kwargs)
93
+ self.class.new(**new_data)
94
+ end
95
+
96
+ # @return [Boolean]
97
+ def ==(other)
98
+ hash_for_comparation == other.hash_for_comparation
99
+ end
100
+
101
+ protected
102
+
103
+ def hash_for_comparation
104
+ { type: self.class, data: @data }
105
+ end
106
+
107
+ private
108
+
109
+ # @return [String]
110
+ def inspect_members
111
+ @data.map do |key, value|
112
+ if key =~ /\A[a-zA-Z_][a-zA-Z0-9_]*\z/
113
+ "#{key}=#{value.inspect}"
114
+ else
115
+ "#{key.inspect}=#{value.inspect}"
116
+ end
117
+ end.join(', ')
118
+ end
119
+ end
120
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DataClassFactory
2
- VERSION = "0.0.beta0"
4
+ VERSION = '0.1.0'
3
5
  end
@@ -1,6 +1,40 @@
1
- require "data_class_factory/version"
1
+ # frozen_string_literal: true
2
2
 
3
+ require 'data_class_factory/version'
4
+ require 'data_class/definition'
5
+ require 'data_class/factory'
6
+ require 'data_class/instance_methods'
7
+
8
+ # Defining a factory class.
9
+ # `Data = DataClassFactory.define_factory_class` will define a factory class like this.
10
+ #
11
+ # class Data
12
+ # # @param attribute_names [Array<Symbol>]
13
+ # # @return [Class<Data>]
14
+ # def self.define(*attribute_names, &block)
15
+ # factory = DataClass::Factory.new(attribute_names)
16
+ # factory.create(parent_class: self, &block)
17
+ # end
18
+ # ...
19
+ # end
20
+ #
3
21
  module DataClassFactory
4
- class Error < StandardError; end
5
- # Your code goes here...
22
+ def backport?
23
+ Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.2.0')
24
+ end
25
+
26
+ def define_factory_class
27
+ Class.new do
28
+ define_singleton_method(:define) do |*attribute_names, &block|
29
+ factory = DataClass::Factory.new(attribute_names)
30
+ factory.create(parent_class: self, &block)
31
+ end
32
+ private_class_method :new
33
+
34
+ include DataClass::InstanceMethods
35
+ end
36
+ end
37
+ module_function :backport?, :define_factory_class
6
38
  end
39
+
40
+ require_relative './data'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: data_class_factory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.beta0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-05 00:00:00.000000000 Z
11
+ date: 2023-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
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: pry
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'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rake
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -39,7 +67,21 @@ dependencies:
39
67
  - !ruby/object:Gem::Version
40
68
  version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
- name: minitest
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-minitest
43
85
  requirement: !ruby/object:Gem::Requirement
44
86
  requirements:
45
87
  - - ">="
@@ -66,6 +108,10 @@ files:
66
108
  - bin/console
67
109
  - bin/setup
68
110
  - data_class_factory.gemspec
111
+ - lib/data.rb
112
+ - lib/data_class/definition.rb
113
+ - lib/data_class/factory.rb
114
+ - lib/data_class/instance_methods.rb
69
115
  - lib/data_class_factory.rb
70
116
  - lib/data_class_factory/version.rb
71
117
  homepage: https://github.com/YusukeIwaki/data_class_factory
@@ -83,12 +129,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
83
129
  version: '2.7'
84
130
  required_rubygems_version: !ruby/object:Gem::Requirement
85
131
  requirements:
86
- - - ">"
132
+ - - ">="
87
133
  - !ruby/object:Gem::Version
88
- version: 1.3.1
134
+ version: '0'
89
135
  requirements: []
90
136
  rubygems_version: 3.4.6
91
137
  signing_key:
92
138
  specification_version: 4
93
- summary: Backport `Data.define` for Ruby < 3.2
139
+ summary: Backport `Data.define` for Ruby 2.7, 3.0, 3.1
94
140
  test_files: []