# kitchen_sink.rb — a big Ruby syntax exercise file

# --- Constants, globals, class vars, instance vars
$global_var = :glob
GLOBAL_CONST = 123
AnotherConst = { a: 1, b: 2 }

# --- Basic literals
int_lit = 42
float_lit = 3.1415
big_int = 123_456_789_012_345_678
ratio = Rational(2, 3)
cplx = Complex(2, -5)
bool_t = true
bool_f = false
nil_val = nil


# --- Strings and symbols
s1 = "double-quoted string with escape\nand interpolation: #{int_lit}"
s2 = 'single-quoted string with \\n literal'
s3 = %Q{interpolated %Q string #{1 + 1}}
s4 = %q{non-interpolated %q string}
sym1 = :symbol
sym2 = :"spaced symbol"
sym3 = %s{another_symbol}

# --- Heredocs
heredoc1 = <<~RUBY
  def sample(x)
    x * 2
  end
RUBY

heredoc2 = <<-'TXT'
literal heredoc with \n not interpreted
TXT

# --- Regex, ranges
re = /foo\d+/i
re2 = %r{^/api/v1/.*$}
range_inc = 1..5
range_exc = 1...5
char_range = 'a'..'f'

# --- Arrays, hashes, keywords splats
arr = [1, 2, 3, *[4, 5], 6]
hsh = { a: 1, 'b' => 2, **{ c: 3 } }
nested = [{ x: [1, 2, { y: :z }] }, 99]

# --- Multiple assignment / destructuring
x, y, z = 1, 2, 3
(a1, (a2, a3)), a4 = [10, [20, 30]], 40

# --- Procs, lambdas, blocks
pr = Proc.new { |u| u * 2 }
lm = ->(u) { u + 3 }

def takes_block(a, b)
  yield a + b if block_given?
end

res1 = takes_block(2, 3) { |v| v * 10 }

# --- Enumerators
enum = [1, 2, 3].map
enum.each { |e| e * 2 }
(1..5).lazy.map { |i| i * i }.take(3).force

# --- Control flow
if int_lit > 40
  msg = "greater"
elsif int_lit == 40
  msg = "equal"
else
  msg = "less"
end

msg2 = "ok" unless bool_f
msg3 = "not ok" if !bool_t

case int_lit
when 1..10
  range_case = :small
when 11, 42
  range_case = :special
else
  range_case = :other
end

i = 0
while i < 3
  i += 1
end

j = 3
until j == 0
  j -= 1
end

for k in [1, 2, 3]
  k
end

loop do
  break
end

# redo example (rare!)
redo_counter = 0
[1, 2, 3].each do |n|
  redo_counter += 1
  redo if redo_counter < 1 && n == 1
end

# --- Methods: positional, defaults, keyword, splats
def args_demo(a, b = 2, *rest, c:, d: 4, **kw, &blk)
  blk.call(a + b + (c || 0) + d) if blk
  [a, b, rest, c, d, kw]
end

args_demo(1, 2, 3, 4, c: 10, d: 20, e: 99) { |sum| sum }

# --- Pattern matching (Ruby 2.7+)
person = { name: "Ada", age: 31, meta: { tags: %w[ruby math] } }

case person
in { name:, age: 31, meta: { tags: [first_tag, *rest_tags] } }
  matched = [name, first_tag, rest_tags]
else
  matched = :no
end

# pin operator
expected = 31
case person
in { age: ^expected }
  pinned = :ok
else
  pinned = :nope
end

# array patterns
arr_pat = [1, 2, 3, 4]
case arr_pat
in [first, 2, *tail]
  pattern_arr = [first, tail]
else
  pattern_arr = []
end

# --- Modules, mixins, refinements
module Mixin
  def mixed_in
    :mixed
  end
end

module OtherMixin
  def other_mixed
    :other
  end
end

module RefinementExample
  refine String do
    def shout
      upcase + "!"
    end
  end
end

using RefinementExample
refined_val = "hey".shout

# --- Classes, attrs, visibility, singleton methods, eigenclass
class Base
  include Mixin
  extend OtherMixin

  CONST_IN_CLASS = :klass

  @@class_var = 0
  @class_inst = :ci

  attr_reader :x
  attr_writer :y
  attr_accessor :z

  def initialize(x, y: 0, **kw)
    @x = x
    @y = y
    @z = kw[:z]
  end

  def self.build(*a, **k)
    new(*a, **k)
  end

  class << self
    def in_eigen
      :eigen
    end
  end

  def public_m
    :public_method
  end

  protected

  def prot_m; :protected_method end

  private

  def priv_m; :private_method end
end

obj = Base.build(10, y: 20, z: 30)
obj.z = 99
_ = obj.mixed_in
_ = Base.other_mixed
_ = Base.in_eigen

def obj.singleton_thing
  :single
end

# --- Inheritance, super, prepend
module PrependDemo
  def public_m
    [:prepended, super]
  end
end

class Child < Base
  prepend PrependDemo

  def initialize(x, y: 0, **kw)
    super
    @child_only = true
  end
end

child = Child.new(1, y: 2, z: 3)
_ = child.public_m

# --- Structs and OpenStruct-like
Point = Struct.new(:x, :y) do
  def length
    Math.sqrt(x * x + y * y)
  end
end

p0 = Point.new(3, 4)
_ = p0.length

# --- Operators as methods
class Vec
  attr_reader :x, :y
  def initialize(x, y) @x, @y = x, y end
  def +(o) Vec.new(x + o.x, y + o.y) end
  def -@( ) Vec.new(-x, -y) end
  def [](i) i == 0 ? x : y end
  def []=(i, v) i == 0 ? @x = v : @y = v end
  def to_s() "(#{x},#{y})" end
end

v1 = Vec.new(1, 2)
v2 = Vec.new(3, 4)
v3 = v1 + v2
v4 = -v3
_  = v4[0]
v4[1] = 99

# --- method_missing / respond_to_missing?
class Ghost
  def method_missing(name, *a, **k, &b)
    return :dynamic if name.to_s.start_with?("dyn_")
    super
  end
  def respond_to_missing?(name, inc = false)
    name.to_s.start_with?("dyn_") || super
  end
end

g = Ghost.new
_ = g.dyn_hello

# --- Exception handling
def risky(x)
  raise ArgumentError, "bad!" if x < 0
  x * 2
rescue ArgumentError => e
  :rescued
ensure
  :ensured
end

begin
  risky(-1)
rescue => e
  # retry demo
  @tries ||= 0
  @tries += 1
  retry if @tries < 1
ensure
  noop = :done
end

# --- Fibers and Threads
fib = Fiber.new do
  a = Fiber.yield 1
  b = Fiber.yield a + 1
  a + b
end
f1 = fib.resume
f2 = fib.resume(10)
f3 = fib.resume(5)

t = Thread.new { (1..3).map { |n| n * n } }
thr_val = t.value

# --- Backticks, %x, system, __FILE__/__LINE__/__ENCODING__
file_meta = [__FILE__, __LINE__, __ENCODING__]
# cmd_out = `echo hi`         # (avoid side-effects in a test file)
# cmd2 = %x(printf "%s" hello)
# system("true")

# --- Keyword lists, %w/%i
words = %w[alpha beta gamma]
syms = %i[al be ga]

# --- Hash with default proc
count = Hash.new { |h, k| h[k] = 0 }
%w[a b a c b a].each { |ch| count[ch] += 1 }

# --- Freeze, dup, clone
frozen = "abc".freeze
duped = frozen.dup
cloned = frozen.clone rescue :cloned

# --- Marshaling (basic)
dumped = Marshal.dump([1, 2, 3])
loaded = Marshal.load(dumped)

# --- Encoding / force_encoding
utf = "héllo"
forced = utf.dup.force_encoding("ASCII-8BIT")

# --- Numeric methods, time
num = 12.5.round
now = Time.now
later = now + 60

# --- Random, ranges, case equality ===
rng = Random.new(1234)
rand_val = rng.rand(1..10)
in_range = (1..10) === rand_val

# --- Refinement scope check
using RefinementExample
refined_again = "yo".shout

# --- %r advanced, named captures
md = /(?<word>\w+)-(?<num>\d+)/.match("abc-123")
cap_word = md[:word] if md

# --- BEGIN/END blocks
BEGIN {
  # Runs before everything (when file is loaded)
  _begin_marker = :begin_block
}

END {
  # Runs at process exit
  _end_marker = :end_block
}

# --- Numeric literals: hex, oct, bin
hex = 0xff
oct = 0o755
bin = 0b101010

# --- Case with guards
val = 10
case val
when Integer if val.even?
  case_guard = :even_int
else
  case_guard = :other
end

# --- Ternary, safe nav, assignment forms
tern = val > 5 ? :big : :small
safe_len = nil&.to_s&.length
x_plus_eq = 5
x_plus_eq += 3

# --- Ranges of strings, flip-flop (obscure)
str_rng = "a".."z"
ff_out = []
i = 0
(1..20).each do |n|
  i += 1 if (n == 3)..(n == 6) # flip-flop
  ff_out << i
end

# --- Here-doc with interpolation & indentation
name = "World"
msg_hd = <<~MSG
  Hello, #{name}!
  The time is #{Time.now}
MSG

# --- Files (read-only sample)
# File.read(__FILE__) rescue nil

# --- Refinements: nested using
module Outer
  module Ref
    refine Integer do
      def add2; self + 2 end
    end
  end
  def self.demo
    using Ref
    5.add2
  end
end
_ = Outer.demo

# --- Keyword argument forwarding (Ruby 3)
def kf(**kw) kw end
def wrapper(**kw) kf(**kw) end
_ = wrapper(a: 1, b: 2)

# --- Pattern matching nested hashes and arrays again
data = {
  user: { id: 1, name: "Ada", roles: %w[admin editor] },
  meta: { active: true }
}

case data
in { user: { id:, name:, roles: ["admin", *rest] }, meta: { active: true } }
  matched_user = [id, name, rest]
else
  matched_user = nil
end

# --- Anonymous class & module
Anon = Class.new do
  def call; :anon end
end
ModAnon = Module.new do
  def mod_call; :mod_anon end
end
class Anon
  include ModAnon
end
_ = Anon.new.call
_ = Anon.new.mod_call

# --- Numeric refinements w/ prepend class method hook
module P
  def self.prepended(klass); @hooked = klass end
  def to_s; "P(#{super})" end
end

class NWrap
  prepend P
  def to_s; "NWrap" end
end

_ = NWrap.new.to_s

# --- Case equality custom
class Matcher
  def ===(other) other.is_a?(String) && other.start_with?("ok") end
end

case "ok-go"
when Matcher.new
  _ = :matched
end

# --- Frozen string literal magic comment (simulated usage)
fsl = +"mutable" # unary + dup

# --- Rescue modifier
risky_value = (1 / 0) rescue :div_by_zero

# --- tap/yield_self/then
tapped = { a: 1 }.tap { |h| h[:b] = 2 }
ys = 5.yield_self { |n| n * 10 }
thn = 10.then { |n| n + 1 }

# --- Method aliasing
class AliasDemo
  def orig; :orig end
  alias :alt :orig
end
_ = AliasDemo.new.alt

# --- Method visibility change
class VisDemo
  def a; :a end
  def b; :b end
  private :b
end

# --- Keyword args + default nil + double splat
def kcombo(a, b: nil, **opt) [a, b, opt] end
_ = kcombo(5, b: 7, c: 9)

# --- Hash pattern with defaults via fetch
hdef = { foo: 1 }
hdef_v = hdef.fetch(:bar, :default)

# --- Refinement + method lookup sanity
module NumRef
  refine Numeric do
    def sq; self * self end
  end
end
using NumRef
_ = 6.sq

# --- rescue with multiple types
begin
  raise IOError, "io"
rescue SystemCallError, IOError => e
  @got = e.class
end

# --- ensure altering outer variable
outer = 0
begin
  outer = 1
ensure
  outer += 1
end

# --- Symbol to proc, &: syntax
doubled = [1, 2, 3].map(&:to_s)

# --- Safe pattern matching nil
case nil
in Integer
  :nope
else
  :ok_nil
end

# --- Hash literal with trailing comma, labeled args-like
cfg = {
  host: "localhost",
  port: 5432,
}

# --- Numeric separators and exponents
exp = 1.23e-4
big = 1_000_000

# --- %i/%w with interpolation (only in %W)
inter = "X"
words2 = %W[a b #{inter}]

# --- Dir, ENV (read only)
_ = Dir.pwd
_ = ENV["SHELL"]

# --- END of the kitchen sink
puts "Loaded kitchen_sink.rb with many Ruby constructs." if __FILE__ == $0
