Skip to content

Commit 5e062b4

Browse files
committed
Add initial implementation of Module#const_set_p.
1 parent bb072ba commit 5e062b4

7 files changed

Lines changed: 109 additions & 10 deletions

File tree

lib/const_set_p.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
# frozen_string_literal: true
22

3+
require_relative "const_set_p/core_ext"
34
require_relative "const_set_p/version"
4-
5-
module ConstSetP
6-
class Error < StandardError; end
7-
# Your code goes here...
8-
end

lib/const_set_p/core_ext.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "core_ext/module"

lib/const_set_p/core_ext/module.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
class Module
4+
def const_set_p(name, value)
5+
first, rest = name.to_s.split("::", 2)
6+
return const_set(first, value) unless rest
7+
8+
submod =
9+
if const_defined?(first, false)
10+
const_get(first, false)
11+
else
12+
const_set(first, Module.new)
13+
end
14+
raise NameError, "#{self.name}::#{first} is not a Module or Class" unless submod.is_a?(Module)
15+
16+
submod.const_set_p(rest, value)
17+
end
18+
end

sig/const_set_p.rbs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
module ConstSetP
22
VERSION: String
3-
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
43
end

sig/module.rbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Module
2+
def const_set_p: (interned name, untyped value) -> untyped
3+
end
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# frozen_string_literal: true
2+
3+
require "securerandom"
4+
5+
RSpec.describe Module do
6+
describe "#const_set_p" do
7+
subject { mod.const_set_p(name, value) }
8+
9+
let(:mod) do
10+
Module.new do
11+
const_set(:C, Class.new)
12+
const_set(:S, "string")
13+
end
14+
end
15+
let(:value) { SecureRandom.uuid }
16+
17+
context 'when the name does not contain "::"' do
18+
let(:name) { "N" }
19+
20+
it "defines M::N and it returns value" do
21+
subject
22+
expect(mod::N).to eq value
23+
end
24+
end
25+
26+
context 'when the name contains "::"' do
27+
context "when the name does not contain the name of a pre-defined constant" do
28+
let(:name) { "N::O" }
29+
30+
it "defines M::N and M::N::O and it returns Module" do
31+
subject
32+
expect(mod::N).to be_a Module
33+
end
34+
35+
it "defines M::N::O and it returns value" do
36+
subject
37+
expect(mod::N::O).to eq value
38+
end
39+
end
40+
41+
context "when the name contains the name of a pre-defined constant" do
42+
context "when the type of constant is a Class or Module" do
43+
let(:name) { "C::D" }
44+
45+
it "does not replace M::C" do
46+
expect { subject }.not_to(change { mod::C })
47+
end
48+
49+
it "defines M::C::D and it returns value" do
50+
subject
51+
expect(mod::C::D).to eq value
52+
end
53+
end
54+
55+
context "when the type of constant is not a Class or Module" do
56+
let(:name) { "S::T" }
57+
58+
it { expect { subject }.to raise_error(NameError) }
59+
end
60+
end
61+
end
62+
end
63+
end

spec/spec_helper.rb

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
# This file was generated by the `rspec --init` command. Conventionally, all
24
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
35
# The generated `.rspec` file contains `--require spec_helper` which will cause
@@ -13,6 +15,25 @@
1315
# it.
1416
#
1517
# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
18+
19+
require "simplecov"
20+
require "simplecov-console"
21+
require "simplecov_json_formatter"
22+
23+
SimpleCov.start do
24+
add_filter "/spec/"
25+
enable_coverage :branch
26+
formatter SimpleCov::Formatter::MultiFormatter.new(
27+
[
28+
SimpleCov::Formatter::JSONFormatter, # for codeclimate
29+
SimpleCov::Formatter::Console # for stdout
30+
]
31+
)
32+
end
33+
34+
require "bundler/setup"
35+
require "const_set_p"
36+
1637
RSpec.configure do |config|
1738
# rspec-expectations config goes here. You can use an alternate
1839
# assertion/expectation library such as wrong or the stdlib/minitest
@@ -44,9 +65,6 @@
4465
# triggering implicit auto-inclusion in groups with matching metadata.
4566
config.shared_context_metadata_behavior = :apply_to_host_groups
4667

47-
# The settings below are suggested to provide a good initial experience
48-
# with RSpec, but feel free to customize to your heart's content.
49-
=begin
5068
# This allows you to limit a spec run to individual examples or groups
5169
# you care about by tagging them with `:focus` metadata. When nothing
5270
# is tagged with `:focus`, all examples get run. RSpec also provides
@@ -94,5 +112,4 @@
94112
# test failures related to randomization by passing the same `--seed` value
95113
# as the one that triggered the failure.
96114
Kernel.srand config.seed
97-
=end
98115
end

0 commit comments

Comments
 (0)