simple-immutable 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.tm_properties +9 -0
- data/Gemfile +4 -0
- data/Makefile +7 -0
- data/README.md +38 -0
- data/VERSION +1 -0
- data/bin/bundle +105 -0
- data/bin/console +8 -0
- data/lib/simple-immutable.rb +1 -0
- data/lib/simple/immutable.rb +148 -0
- data/scripts/release +3 -0
- data/scripts/release.rb +94 -0
- data/simple-immutable.gemspec +25 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ee7113a903d3124b8d4c64499cc3dcd3ae296d6d4b45c6178b5205c3baae8675
|
4
|
+
data.tar.gz: 722814d2e633c8a7ea57c71b5add682ee62f46bffc113ad2047bc1b281dc9516
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a6328926689c3ded5d5e6a3ae4f24b0fcc73663ef0d2aec26019c0ec6fc705c8f7da903fcb9aba8a917c75884b3299f603d3f521af95bf5cd4b7d7dd6f8c2c0c
|
7
|
+
data.tar.gz: adae1e19d19f7ce6a4c152b6137d27689c679d11ca91150bf04fd49115f8ba67ec080f1f2ba965e2e8111a08ce6ef3b58b4ef7bdefbb9730adadd63a6ff434b2
|
data/.gitignore
ADDED
data/.tm_properties
ADDED
data/Gemfile
ADDED
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# simple-immutable
|
2
|
+
|
3
|
+
Turns a nested data structure into a immutable ruby object implementing dot and [] accessors.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
|
8
|
+
Immutable = ::Simple::Immutable
|
9
|
+
|
10
|
+
data =
|
11
|
+
{
|
12
|
+
a: "a-value",
|
13
|
+
"b": "b-value",
|
14
|
+
"child": {
|
15
|
+
name: "childname",
|
16
|
+
grandchild: {
|
17
|
+
name: "grandchildname"
|
18
|
+
}
|
19
|
+
},
|
20
|
+
"children": [
|
21
|
+
"anna",
|
22
|
+
"arthur",
|
23
|
+
{
|
24
|
+
action: {
|
25
|
+
keep_your_mouth_shut: true
|
26
|
+
}
|
27
|
+
}
|
28
|
+
]
|
29
|
+
}
|
30
|
+
|
31
|
+
imm = Immutable.create(data)
|
32
|
+
|
33
|
+
imm.a # -> 'a-value'
|
34
|
+
imm.children.class # -> Array
|
35
|
+
imm.children[2].action.keep_your_mouth_shut # -> true
|
36
|
+
imm.children.foo # NoMethodError (undefined method `foo' for #<Array:0x00007fb90f1be390>)
|
37
|
+
imm.children.first.foo # NoMethodError (undefined method `foo' for "anna":String)
|
38
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.1
|
data/bin/bundle
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "rubygems"
|
12
|
+
|
13
|
+
m = Module.new do
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def invoked_as_script?
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def env_var_version
|
21
|
+
ENV["BUNDLER_VERSION"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def cli_arg_version
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
27
|
+
bundler_version = nil
|
28
|
+
update_index = nil
|
29
|
+
ARGV.each_with_index do |a, i|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
31
|
+
bundler_version = a
|
32
|
+
end
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
34
|
+
bundler_version = $1 || ">= 0.a"
|
35
|
+
update_index = i
|
36
|
+
end
|
37
|
+
bundler_version
|
38
|
+
end
|
39
|
+
|
40
|
+
def gemfile
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
43
|
+
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
45
|
+
end
|
46
|
+
|
47
|
+
def lockfile
|
48
|
+
lockfile =
|
49
|
+
case File.basename(gemfile)
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
51
|
+
else "#{gemfile}.lock"
|
52
|
+
end
|
53
|
+
File.expand_path(lockfile)
|
54
|
+
end
|
55
|
+
|
56
|
+
def lockfile_version
|
57
|
+
return unless File.file?(lockfile)
|
58
|
+
lockfile_contents = File.read(lockfile)
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
60
|
+
Regexp.last_match(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
def bundler_version
|
64
|
+
@bundler_version ||= begin
|
65
|
+
env_var_version || cli_arg_version ||
|
66
|
+
lockfile_version || "#{Gem::Requirement.default}.a"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_bundler!
|
71
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
72
|
+
|
73
|
+
# must dup string for RG < 1.8 compatibility
|
74
|
+
activate_bundler(bundler_version.dup)
|
75
|
+
end
|
76
|
+
|
77
|
+
def activate_bundler(bundler_version)
|
78
|
+
if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
|
79
|
+
bundler_version = "< 2"
|
80
|
+
end
|
81
|
+
gem_error = activation_error_handling do
|
82
|
+
gem "bundler", bundler_version
|
83
|
+
end
|
84
|
+
return if gem_error.nil?
|
85
|
+
require_error = activation_error_handling do
|
86
|
+
require "bundler/version"
|
87
|
+
end
|
88
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
89
|
+
warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
|
90
|
+
exit 42
|
91
|
+
end
|
92
|
+
|
93
|
+
def activation_error_handling
|
94
|
+
yield
|
95
|
+
nil
|
96
|
+
rescue StandardError, LoadError => e
|
97
|
+
e
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
m.load_bundler!
|
102
|
+
|
103
|
+
if m.invoked_as_script?
|
104
|
+
load Gem.bin_path("bundler", "bundle")
|
105
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "simple/immutable"
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Simple
|
2
|
+
end
|
3
|
+
|
4
|
+
class Simple::Immutable
|
5
|
+
SELF = self
|
6
|
+
|
7
|
+
# turns an object, which can be a hash or array of hashes, arrays, and scalars
|
8
|
+
# into an object which you can use to access with dot methods.
|
9
|
+
def self.create(object, max_depth = 5)
|
10
|
+
case object
|
11
|
+
when Array
|
12
|
+
raise ArgumentError, "Object nested too deep (or inner loop?)" if max_depth < 0
|
13
|
+
|
14
|
+
object.map { |obj| create obj, max_depth - 1 }
|
15
|
+
when Hash
|
16
|
+
new(object)
|
17
|
+
else
|
18
|
+
object
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def initialize(hsh)
|
25
|
+
@hsh = hsh
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(sym, *args, &block)
|
29
|
+
if args.empty? && !block
|
30
|
+
begin
|
31
|
+
value = @hsh.fetch(sym.to_sym) { @hsh.fetch(sym.to_s) }
|
32
|
+
return SELF.create(value)
|
33
|
+
rescue KeyError
|
34
|
+
# STDERR.puts "Missing attribute #{sym} for Immutable w/#{@hsh.inspect}"
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
public
|
43
|
+
|
44
|
+
def respond_to_missing?(method_name, include_private = false)
|
45
|
+
@hsh.key?(method_name.to_sym) ||
|
46
|
+
@hsh.key?(method_name.to_s) ||
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def inspect
|
51
|
+
"<Immutable: #{@hsh.inspect}>"
|
52
|
+
end
|
53
|
+
|
54
|
+
def respond_to?(sym)
|
55
|
+
super || @hsh.key?(sym.to_s) || @hsh.key?(sym.to_sym)
|
56
|
+
end
|
57
|
+
|
58
|
+
def ==(other)
|
59
|
+
@hsh == other
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if $PROGRAM_NAME == __FILE__
|
64
|
+
|
65
|
+
# rubocop:disable Metrics/AbcSize
|
66
|
+
|
67
|
+
require "test-unit"
|
68
|
+
|
69
|
+
class Simple::Immutable::TestCase < Test::Unit::TestCase
|
70
|
+
Immutable = ::Simple::Immutable
|
71
|
+
|
72
|
+
def hsh
|
73
|
+
{
|
74
|
+
a: "a-value",
|
75
|
+
"b": "b-value",
|
76
|
+
"child": {
|
77
|
+
name: "childname",
|
78
|
+
grandchild: {
|
79
|
+
name: "grandchildname"
|
80
|
+
}
|
81
|
+
},
|
82
|
+
"children": [
|
83
|
+
"anna",
|
84
|
+
"arthur",
|
85
|
+
{
|
86
|
+
action: {
|
87
|
+
keep_your_mouth_shut: true
|
88
|
+
}
|
89
|
+
}
|
90
|
+
]
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def immutable
|
95
|
+
Immutable.create hsh
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_hash_access
|
99
|
+
assert_equal "a-value", immutable.a
|
100
|
+
assert_equal "b-value", immutable.b
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_comparison
|
104
|
+
immutable = Immutable.create hsh
|
105
|
+
|
106
|
+
assert_equal immutable, hsh
|
107
|
+
assert_not_equal({}, immutable)
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_child_access
|
111
|
+
child = immutable.child
|
112
|
+
assert_kind_of(Immutable, child)
|
113
|
+
assert_equal "childname", immutable.child.name
|
114
|
+
assert_equal "grandchildname", immutable.child.grandchild.name
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_array_access
|
118
|
+
assert_kind_of(Array, immutable.children)
|
119
|
+
assert_equal 3, immutable.children.length
|
120
|
+
assert_equal "anna", immutable.children[0]
|
121
|
+
|
122
|
+
assert_kind_of(Immutable, immutable.children[2])
|
123
|
+
assert_equal true, immutable.children[2].action.keep_your_mouth_shut
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_base_class
|
127
|
+
assert_nothing_raised do
|
128
|
+
immutable.object_id
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_missing_keys
|
133
|
+
assert_raise(NoMethodError) do
|
134
|
+
immutable.foo
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_skip_when_args_or_block
|
139
|
+
assert_raise(NoMethodError) do
|
140
|
+
immutable.a(1, 2, 3)
|
141
|
+
end
|
142
|
+
assert_raise(NoMethodError) do
|
143
|
+
immutable.a { :dummy }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
data/scripts/release
ADDED
data/scripts/release.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# -- helpers ------------------------------------------------------------------
|
4
|
+
|
5
|
+
def sys(cmd)
|
6
|
+
STDERR.puts "> #{cmd}"
|
7
|
+
system cmd
|
8
|
+
return true if $?.success?
|
9
|
+
|
10
|
+
STDERR.puts "> #{cmd} returned with exitstatus #{$?.exitstatus}"
|
11
|
+
$?.success?
|
12
|
+
end
|
13
|
+
|
14
|
+
def sys!(cmd, error: nil)
|
15
|
+
return true if sys(cmd)
|
16
|
+
STDERR.puts error if error
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def die!(msg)
|
21
|
+
STDERR.puts msg
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
ROOT = File.expand_path("#{File.dirname(__FILE__)}/..")
|
26
|
+
|
27
|
+
GEMSPEC = Dir.glob("*.gemspec").first || die!("Missing gemspec file.")
|
28
|
+
|
29
|
+
# -- Version reading and bumping ----------------------------------------------
|
30
|
+
|
31
|
+
module Version
|
32
|
+
extend self
|
33
|
+
|
34
|
+
VERSION_FILE = "#{Dir.getwd}/VERSION"
|
35
|
+
|
36
|
+
def read_version
|
37
|
+
version = File.exist?(VERSION_FILE) ? File.read(VERSION_FILE) : "0.0.1"
|
38
|
+
version.chomp!
|
39
|
+
raise "Invalid version number in #{VERSION_FILE}" unless version =~ /^\d+\.\d+\.\d+$/
|
40
|
+
version
|
41
|
+
end
|
42
|
+
|
43
|
+
def auto_version_bump
|
44
|
+
old_version_number = read_version
|
45
|
+
old = old_version_number.split('.')
|
46
|
+
|
47
|
+
current = old[0..-2] << old[-1].next
|
48
|
+
current.join('.')
|
49
|
+
end
|
50
|
+
|
51
|
+
def bump_version
|
52
|
+
next_version = ENV["VERSION"] || auto_version_bump
|
53
|
+
File.open(VERSION_FILE, "w") { |io| io.write next_version }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# -- check, bump, release a new gem version -----------------------------------
|
58
|
+
|
59
|
+
Dir.chdir ROOT
|
60
|
+
$BASE_BRANCH = ENV['BRANCH'] || 'master'
|
61
|
+
|
62
|
+
# ENV["BUNDLE_GEMFILE"] = "#{Dir.getwd}/Gemfile"
|
63
|
+
# sys! "bundle install"
|
64
|
+
|
65
|
+
sys! "git diff --exit-code > /dev/null", error: 'There are unstaged changes in your working directory'
|
66
|
+
sys! "git diff --cached --exit-code > /dev/null", error: 'There are staged but uncommitted changes'
|
67
|
+
|
68
|
+
sys! "git checkout #{$BASE_BRANCH}"
|
69
|
+
sys! "git pull"
|
70
|
+
|
71
|
+
Version.bump_version
|
72
|
+
version = Version.read_version
|
73
|
+
|
74
|
+
sys! "git add VERSION"
|
75
|
+
sys! "git commit -m \"bump gem to v#{version}\""
|
76
|
+
sys! "git tag -a v#{version} -m \"Tag #{version}\""
|
77
|
+
|
78
|
+
sys! "git push origin #{$BASE_BRANCH}"
|
79
|
+
sys! 'git push --tags --force'
|
80
|
+
|
81
|
+
sys! "gem build #{GEMSPEC}"
|
82
|
+
sys! "mkdir -p pkg"
|
83
|
+
sys! "mv *.gem pkg"
|
84
|
+
|
85
|
+
gem_file = Dir.glob('pkg/*.gem').sort.last
|
86
|
+
|
87
|
+
sys! "gem install #{gem_file}"
|
88
|
+
sys! "gem push #{gem_file}"
|
89
|
+
|
90
|
+
STDERR.puts <<-MSG
|
91
|
+
================================================================================
|
92
|
+
Thank you for releasing a new gem version. You made my day.
|
93
|
+
================================================================================
|
94
|
+
MSG
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This file is part of the sinatra-sse ruby gem.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2016, 2017 @radiospiel, mediapeers Gem
|
4
|
+
# Distributed under the terms of the modified BSD license, see LICENSE.BSD
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "simple-immutable"
|
8
|
+
gem.version = File.read "VERSION"
|
9
|
+
|
10
|
+
gem.authors = [ "radiospiel", "mediapeers GmbH" ]
|
11
|
+
gem.email = "eno@radiospiel.org"
|
12
|
+
gem.homepage = "http://github.com/radiospiel/simple-immutable"
|
13
|
+
gem.summary = "Immutable ruby objects"
|
14
|
+
|
15
|
+
gem.description = "Immutable ruby objects implementing dot and [] accessors."
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = %w(lib)
|
20
|
+
|
21
|
+
# executables are used for development purposes only
|
22
|
+
gem.executables = []
|
23
|
+
|
24
|
+
gem.required_ruby_version = '~> 2.3'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple-immutable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- radiospiel
|
8
|
+
- mediapeers GmbH
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2020-03-03 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Immutable ruby objects implementing dot and [] accessors.
|
15
|
+
email: eno@radiospiel.org
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".gitignore"
|
21
|
+
- ".tm_properties"
|
22
|
+
- Gemfile
|
23
|
+
- Makefile
|
24
|
+
- README.md
|
25
|
+
- VERSION
|
26
|
+
- bin/bundle
|
27
|
+
- bin/console
|
28
|
+
- lib/simple-immutable.rb
|
29
|
+
- lib/simple/immutable.rb
|
30
|
+
- scripts/release
|
31
|
+
- scripts/release.rb
|
32
|
+
- simple-immutable.gemspec
|
33
|
+
homepage: http://github.com/radiospiel/simple-immutable
|
34
|
+
licenses: []
|
35
|
+
metadata: {}
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - "~>"
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '2.3'
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubygems_version: 3.0.6
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Immutable ruby objects
|
55
|
+
test_files: []
|