# 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 = /(?\w+)-(?\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