ks 0.0.1 → 0.0.2
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/.gitignore +50 -0
- data/.rubocop.yml +10 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -7
- data/Rakefile +11 -44
- data/ks.gemspec +40 -0
- data/lib/ks.rb +55 -8
- metadata +42 -27
- data/spec/ks_spec.rb +0 -28
- data/spec/spec_helper.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ac4b409cd2da70b14d2bd80734bcec3cbb69e3a
|
4
|
+
data.tar.gz: 8766f1ce550dd060e7cc27997974f1a4eecdd846
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5fd595264b15cb35fa9d6ef0b6269c7ca79910df4cc65d02fd73e53a7c7180a2b5b6cf91bce83de035f9e15fabc48bf73b73c4ee935de9315fb01d943804244
|
7
|
+
data.tar.gz: a15d78f2e7340bb1013152b7146832eb2fd30e51c5b98f357be1dcbe1c1c6bc74210cd7d4cbb22c3b3228e3fec930dbdbc292052d96e5c0a1c4caa4d7ee28177
|
data/.gitignore
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
coverage.data
|
4
|
+
|
5
|
+
# rdoc generated
|
6
|
+
rdoc
|
7
|
+
|
8
|
+
# yard generated
|
9
|
+
doc
|
10
|
+
.yardoc
|
11
|
+
|
12
|
+
# bundler
|
13
|
+
.bundle
|
14
|
+
Gemfile.lock
|
15
|
+
|
16
|
+
# jeweler generated
|
17
|
+
pkg
|
18
|
+
|
19
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
20
|
+
#
|
21
|
+
# * Create a file at ~/.gitignore
|
22
|
+
# * Include files you want ignored
|
23
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
24
|
+
#
|
25
|
+
# After doing this, these files will be ignored in all your git projects,
|
26
|
+
# saving you from having to 'pollute' every project you touch with them
|
27
|
+
#
|
28
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
29
|
+
#
|
30
|
+
# For MacOS:
|
31
|
+
#
|
32
|
+
#.DS_Store
|
33
|
+
|
34
|
+
# For TextMate
|
35
|
+
#*.tmproj
|
36
|
+
#tmtags
|
37
|
+
|
38
|
+
# For emacs:
|
39
|
+
#*~
|
40
|
+
#\#*
|
41
|
+
#.\#*
|
42
|
+
|
43
|
+
# For vim:
|
44
|
+
#*.swp
|
45
|
+
|
46
|
+
# For redcar:
|
47
|
+
#.redcar
|
48
|
+
|
49
|
+
# For rubinius:
|
50
|
+
#*.rbc
|
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,51 +1,18 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require 'bundler'
|
5
|
-
begin
|
6
|
-
Bundler.setup(:default, :development)
|
7
|
-
rescue Bundler::BundlerError => e
|
8
|
-
$stderr.puts e.message
|
9
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
-
exit e.status_code
|
11
|
-
end
|
12
|
-
require 'rake'
|
13
|
-
|
14
|
-
require_relative 'lib/ks'
|
15
|
-
require 'jeweler'
|
16
|
-
Jeweler::Tasks.new do |gem|
|
17
|
-
gem.version = Ks::VERSION
|
18
|
-
gem.name = "ks"
|
19
|
-
gem.homepage = "http://github.com/wetransfer/ks"
|
20
|
-
gem.license = "MIT"
|
21
|
-
gem.description = %Q{Keyword-initialized Structs}
|
22
|
-
gem.description = %Q{Keyword-initialized Structs}
|
23
|
-
gem.email = "me@julik.nl"
|
24
|
-
gem.authors = ["Julik Tarkhanov"]
|
25
|
-
# dependencies defined in Gemfile
|
26
|
-
end
|
27
|
-
Jeweler::RubygemsDotOrgTasks.new
|
28
|
-
|
29
|
-
require 'rspec/core'
|
3
|
+
require 'bundler/gem_tasks'
|
30
4
|
require 'rspec/core/rake_task'
|
31
|
-
|
32
|
-
|
33
|
-
end
|
5
|
+
require 'yard'
|
6
|
+
require 'rubocop/rake_task'
|
34
7
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
8
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
9
|
+
# The dash has to be between the two to "divide" the source files and
|
10
|
+
# miscellaneous documentation files that contain no code
|
11
|
+
t.files = ['lib/**/*.rb', '-', 'LICENSE.txt', 'IMPLEMENTATION_DETAILS.md']
|
39
12
|
end
|
40
13
|
|
41
|
-
|
14
|
+
RSpec::Core::RakeTask.new(:spec)
|
42
15
|
|
43
|
-
|
44
|
-
Rake::RDocTask.new do |rdoc|
|
45
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
16
|
+
RuboCop::RakeTask.new(:rubocop)
|
46
17
|
|
47
|
-
|
48
|
-
rdoc.title = "ks #{version}"
|
49
|
-
rdoc.rdoc_files.include('README*')
|
50
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
-
end
|
18
|
+
task default: %i[spec rubocop]
|
data/ks.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'ks'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'ks'
|
9
|
+
spec.version = Ks::VERSION
|
10
|
+
spec.authors = ['Julik Tarkhanov']
|
11
|
+
spec.email = ['me@julik.nl']
|
12
|
+
|
13
|
+
spec.summary = 'Keyword-initialized Structs'
|
14
|
+
spec.description = 'Keyword-initialized Structs'
|
15
|
+
spec.homepage = 'http://github.com/WeTransfer/ks'
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org.
|
18
|
+
# To allow pushes either set the 'allowed_push_host'
|
19
|
+
# To allow pushing to a single host or delete
|
20
|
+
# this section to allow pushing to any host.
|
21
|
+
if spec.respond_to?(:metadata)
|
22
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
23
|
+
else
|
24
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
25
|
+
'public gem pushes.'
|
26
|
+
end
|
27
|
+
|
28
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
29
|
+
f.match(%r{^(test|spec|features)/})
|
30
|
+
end
|
31
|
+
spec.bindir = 'exe'
|
32
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
33
|
+
spec.require_paths = ['lib']
|
34
|
+
|
35
|
+
spec.add_development_dependency 'bundler', '~> 1'
|
36
|
+
spec.add_development_dependency 'rake', '~> 12'
|
37
|
+
spec.add_development_dependency 'rspec', '~> 3'
|
38
|
+
spec.add_development_dependency 'wetransfer_style', '0.6.0'
|
39
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
40
|
+
end
|
data/lib/ks.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'set'
|
3
4
|
|
4
5
|
# "Ks" - as in "kiss" - a generator of keyworded Structs.
|
5
6
|
module Ks
|
6
|
-
VERSION = '0.0.
|
7
|
-
|
7
|
+
VERSION = '0.0.2'
|
8
|
+
|
8
9
|
@@caching_mutex = Mutex.new
|
9
10
|
@@predefined_structs = {}
|
10
|
-
|
11
|
+
|
11
12
|
# Returns a class that is a descendant of Struct, with a strict
|
12
13
|
# keyword initializer.
|
13
14
|
#
|
@@ -17,9 +18,10 @@ module Ks
|
|
17
18
|
# Note that all the keyword arguments defined for the class (all the members)
|
18
19
|
# are going to be required keyword arguments for the initializer.
|
19
20
|
#
|
20
|
-
# The created classes (Struct descendants) are cached
|
21
|
-
# since when reloading a usual Struct
|
22
|
-
#
|
21
|
+
# The created classes (Struct descendants) are cached
|
22
|
+
# to make reloading easier, since when reloading a usual Struct
|
23
|
+
# descendant it will receive a different parent class.
|
24
|
+
# This is mitigated by caching the created subclasses using their member lists
|
23
25
|
#
|
24
26
|
# @param members[Array<Symbol>] the names of members to create
|
25
27
|
# @return created_class[Class]
|
@@ -27,7 +29,7 @@ module Ks
|
|
27
29
|
k = members.sort.join(':')
|
28
30
|
@@caching_mutex.synchronize do
|
29
31
|
return @@predefined_structs[k] if @@predefined_structs[k]
|
30
|
-
|
32
|
+
|
31
33
|
struct_ancestor = Struct.new(*members)
|
32
34
|
predefined = Class.new(struct_ancestor) do
|
33
35
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
@@ -40,4 +42,49 @@ module Ks
|
|
40
42
|
predefined
|
41
43
|
end
|
42
44
|
end
|
45
|
+
|
46
|
+
# Returns a class that is a descendant of Struct, with a
|
47
|
+
# keyword initializer that permits unknown keywords to be passed in.
|
48
|
+
# Those keywords will be dropped. The keywords that are known at
|
49
|
+
# definition time will be checked for presence. This allows you to
|
50
|
+
# use structs for API responses and payloads that might get additional
|
51
|
+
# properties as the API evolves, without breaking (your) consuming
|
52
|
+
# code. Imagine at time of designing your structures you specify a
|
53
|
+
# Shipment:
|
54
|
+
#
|
55
|
+
# Shipment = Ks.allowing_unknown(:sku, :weight)
|
56
|
+
#
|
57
|
+
# The API you are using, however, later adds a "shipping_company_id"
|
58
|
+
# property. If you had used `strict` your struct would fail to initialize,
|
59
|
+
# since it does not know about the `shipping_company_id` attribute.
|
60
|
+
#
|
61
|
+
# Shipment.new(JSON.parse(payload)) #=> ArgumentError...
|
62
|
+
#
|
63
|
+
# but as you have used `allowing_unknown` the "shipping_company_id" property
|
64
|
+
# will be silently dropped instead.
|
65
|
+
#
|
66
|
+
# The created classes (Struct descendants) are cached
|
67
|
+
# to make reloading easier, since when reloading a usual Struct
|
68
|
+
# descendant it will receive a different parent class.
|
69
|
+
# This is mitigated by caching the created subclasses using their member lists
|
70
|
+
#
|
71
|
+
# @param members[Array<Symbol>] the names of members to create. Those members will be required, just like with `Ks.strict`
|
72
|
+
# @return created_class[Class]
|
73
|
+
def self.allowing_unknown(*members)
|
74
|
+
k = 'with_optionals:' + members.sort.join(':')
|
75
|
+
@@caching_mutex.synchronize do
|
76
|
+
return @@predefined_structs[k] if @@predefined_structs[k]
|
77
|
+
|
78
|
+
struct_ancestor = Struct.new(*members)
|
79
|
+
predefined = Class.new(struct_ancestor) do
|
80
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
81
|
+
def initialize(#{members.map { |a| "#{a}:" }.join(', ')}, **) # def initialize(bar:, baz:, **)
|
82
|
+
super(#{members.join(', ')}) # super(bar, baz)
|
83
|
+
end # end
|
84
|
+
METHOD
|
85
|
+
end
|
86
|
+
@@predefined_structs[k] = predefined
|
87
|
+
predefined
|
88
|
+
end
|
89
|
+
end
|
43
90
|
end
|
metadata
CHANGED
@@ -1,92 +1,107 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '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:
|
26
|
+
version: '1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '12'
|
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: '
|
40
|
+
version: '12'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '3'
|
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: '
|
54
|
+
version: '3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: wetransfer_style
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.6.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.6.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
75
|
+
version: '0.9'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
82
|
+
version: '0.9'
|
69
83
|
description: Keyword-initialized Structs
|
70
|
-
email:
|
84
|
+
email:
|
85
|
+
- me@julik.nl
|
71
86
|
executables: []
|
72
87
|
extensions: []
|
73
|
-
extra_rdoc_files:
|
74
|
-
- LICENSE.txt
|
75
|
-
- README.md
|
88
|
+
extra_rdoc_files: []
|
76
89
|
files:
|
77
90
|
- ".document"
|
91
|
+
- ".gitignore"
|
78
92
|
- ".rspec"
|
93
|
+
- ".rubocop.yml"
|
94
|
+
- ".travis.yml"
|
79
95
|
- Gemfile
|
80
96
|
- LICENSE.txt
|
81
97
|
- README.md
|
82
98
|
- Rakefile
|
99
|
+
- ks.gemspec
|
83
100
|
- lib/ks.rb
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
- MIT
|
89
|
-
metadata: {}
|
101
|
+
homepage: http://github.com/WeTransfer/ks
|
102
|
+
licenses: []
|
103
|
+
metadata:
|
104
|
+
allowed_push_host: https://rubygems.org
|
90
105
|
post_install_message:
|
91
106
|
rdoc_options: []
|
92
107
|
require_paths:
|
@@ -103,8 +118,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
118
|
version: '0'
|
104
119
|
requirements: []
|
105
120
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.
|
121
|
+
rubygems_version: 2.6.11
|
107
122
|
signing_key:
|
108
123
|
specification_version: 4
|
109
|
-
summary:
|
124
|
+
summary: Keyword-initialized Structs
|
110
125
|
test_files: []
|
data/spec/ks_spec.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
|
3
|
-
describe "Ks" do
|
4
|
-
describe '.strict' do
|
5
|
-
it "allocates a Struct class that can be initialized with keywords" do
|
6
|
-
k = Ks.strict(:foo, :bar)
|
7
|
-
item = k.new(foo: 1, bar: 2)
|
8
|
-
expect(item.foo).to eq(1)
|
9
|
-
expect(item.bar).to eq(2)
|
10
|
-
expect(item.class.ancestors).to include(Struct)
|
11
|
-
expect(item.members).to eq([:foo, :bar])
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'raises when keyword arguments are omitted' do
|
15
|
-
k = Ks.strict(:foo, :bar)
|
16
|
-
expect {
|
17
|
-
k.new(foo: 1)
|
18
|
-
}.to raise_error(ArgumentError, 'missing keyword: bar')
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'caches the created Struct ancestor even when using multiple threads' do
|
22
|
-
classes = (1..12).map do
|
23
|
-
Thread.new { Ks.strict(:one, :another) }
|
24
|
-
end.map(&:join).map(&:value)
|
25
|
-
expect(classes.uniq.length).to eq(1)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|