--- title: Goby prism_languages: [ruby] weight: -3 updated: 2018-12-06 category: Ruby intro: | Goby's language design is based on Ruby language's, slim and shaped up. Differences in syntax between them is very small. --- ## Getting started ### Hello world {: .-prime} #### hello.gb {: .-file} ```ruby class Greet attr_accessor :audience, :head, :tail def initialize @head = "Hello, " @tail = "!" end def name audience.name end def say puts head + name + tail end end module MyName attr_reader :name def initialize @name = self.class.to_s end end class World include MyName end greet = Greet.new greet.audience = World.new greet.say ``` Then run: ```bash $ goby hello.gb #=> Hello, World! ``` ### REPL (igb) ```bash $ goby -i ``` * `reset`: reset the VM * `exit`: exit REPL * `help`: show help * ctrl-c: cancel the block entered, or exit (on top level) See [igb manual & test script](https://github.com/goby-lang/goby/blob/master/igb/manual_test.md). You can use `readline` features such as command history by arrow keys. ## Variables {: .-three-column} ### Local variable ```ruby zip101 = "233-7383" magic_number = 42 ``` Should be "`[a-z][a-z0-9_]+`"(snake_case). ### Instance variable ```ruby module State def initialize(state) @state = state # declaring an instance variable by assignment end def show @state # accessible from other instance methods end end state = State.new "success" state.show #=> success ``` Should be "`@[a-z][a-z0-9_]+`"(snake_case). ### Multiple assignment ```ruby # array literal a, b, c = [1, 2, 3] # array with '*' a = [1, 2, 3] x, y, z = *a # array literal with '*' a, b, c = *[1, 2, 3] # bare assignment: unsupported a, b, c = 1, 2, 3 #=> unexpected 3 Line: 0 ``` ### Black hole variable ```ruby # '_' is write-only a, _ = [1, 2] ``` ### Class variable Unsupported. ### Global variable Unsupported. ## Method definition ### Method definition and calling ```ruby def foo_bar?(baz) if baz == "Hi, Goby!" true else false end end foo_bar? "Hi, Goby!" #=> true ``` Method name should be "`[a-z][a-z0-9_]+\??`" (snake_case). You can omit the trailing "`()`" only if no parameters are taken. Trailing using "`!`" is **unsupported**. ### Order of method parameter ```ruby def foo(normal, default="value", hash={}, ary=[], keyword:, keyword_default:"key", *sprat) end ``` If a default value is provided to a parameter, the parameter can be omitted when calling. `()` can be omitted. The order of parameters in method definition is restricted as follows: 1. **normal parameters** (like `a`) 2. **normal parameters with default value** (like `a=1`) 3. **optional parameters** (array or hash, like `ary=[]` or `hs={}`) 4. **keyword parameters** (like `kwd:`) 5. **keyword parameters with default value** (like `kwd: 1` or `ary: [1,2,3]` or `hsh: {key: "value"}`) 6. **splat parameters** (like `*sp`) Or you will receive an error. ### Keyword parameter (WIP) ```ruby def foo(process:, verb: :GET, opt:{ csp: :enabled }, ary: [1, 2, 3]) end ``` ### Returning value ```ruby PI = 3.14 def area(radius) radius * PI # returns the result of evaluation end area 6 #=> 18.84 ``` ### Returning multiple value ```ruby def my_array [1, 2, 3] end my_array #=> [1, 2, 3] ``` ### Instance method ```ruby module Foo def bar # defining instance method puts "bar" end def baz(count, email: "goby@example.com") count.times do puts email end end end foo = Foo.new foo.bar #=> bar foo.baz(3) #↓ goby@example.com goby@example.com goby@example.com ``` ### Singleton method #1 ```ruby str = "Goby" def str.foo #1 singleton method on the object self * 2 end str.foo #=> GobyGoby ``` ### Singleton method #2 ```ruby module Foo def self.bar #2 singleton method with `self.` 92 end end ``` ### Singleton method #3 ```ruby module Foo def Foo.bar #3 singleton method with a class name (unrecommended) 88 end end ``` ### Singleton method #4 ```ruby module Foo end def Foo.bar #4 singleton methods outside the Foo 9999 end Foo.bar #=> 9999 ``` ### Attribute accessor method ```ruby class Foo attr_accessor :bar, :baz def initialize @bar = 42 @baz = 99 end end foo = Foo.new foo.bar = 77 foo.baz = 88 ``` You can use the following shorthands to declare attribute accessor methods in classes/modules: * `attr_accessor` * `attr_reader` * `attr_writer` ### Private method (to be implemented) ```ruby class Foo def bar 42 end def _baz # leading '_' means private method 99 end end ``` ## Module/Class definition {: .-three-column} ### Module definition and `include` ```ruby module Foo def foo "Foo's instance method" end end class Bar include Foo # to include Foo end Bar.new.foo #=> Foo's instance method ``` Module names should be "`[A-Z][A-Za-z0-9_]+`" (UpperCamelCase). Modules cannot be inherited. ### Module definition and `extend` ```ruby module Foo def foo "Foo's instance method will be a singleton method" end end class Bar extend Foo # to extend Foo end Bar.foo #=> Foo's instance method will be a singleton method ``` `extend` is to use the instance methods in the specified modules as **singleton methods** in your class or module. ### Module instantiation ```ruby module Foo #module definition def foo 99 end end Foo.new.foo #=> 99 ``` Actually, Goby's module can be even **instantiated** via "`new`" like "`Foo.new`". ### Class definition and inheritance ```ruby class Foo # class definition def bar 99 end end class Baz < Foo # inheritance end Baz.new.bar #=> 99 ``` Class names should be "`[A-Z][A-Za-z0-9]+`" (UpperCamelCase). Inheritance with "`<`" is supported. ### Constants ```ruby HTTP_ERROR_404 = 404 HTTP_ERROR_404 = 500 # error ``` Constants should be "`[A-Z][A-Za-z0-9_]+`" (UPPER_SNAKECASE). Constants are **not reentrant** and the scope is **global**. ### Redefining class/modules ```ruby class Foo def bar 99 end end class Foo def bar # redefining is possible 77 end end ``` ### Namespaces ```ruby class Foo module Bar MAGIC = 99 def baz 99 end end end Foo::Bar.new.baz # Use '::' for namespacing Foo::Bar::MAGIC # Use '::' for namespacing ``` ## Load library ### `require` ```ruby require("uri") # to activate URL class u = URI.parse("http://example.com") u.scheme #=> "http" ``` ### `require_relative` ```ruby require_relative("bar") # loading the local bar.gb class Foo def self.bar(x) Bar.foo do |ten| x * ten end end def self.baz yield(100) end end ``` ## Literal {: .-three-column} ### Keyword `def`, `true`, `false`, `nil`, `if`, `elsif`, `else`, `case`, `when`, `return`, `self`, `end`, `while`, `do`, `yield`, `get_block`, `next`, `class`, `module`, `break` ### String literal ```ruby "double quote" 'single quote' ``` Double and single quotation can be used. ### Symbol literal ```ruby :symbol # equivalent to "symbol" { symbol: "value" } ``` Goby's symbol (using `:`) is always `String` class. ### Numeric literal ```ruby year = 2018 # Integer offset = -42 # Integer PI = 3.14 # Float G = -9.8 # Float ``` ### Array literal ```ruby [1, 2, 3, "hello", :goby, { key: "value"}] [1, 2, [3, 4], 5, 6] ``` ### Hash literal ```ruby h = { key: "value", key2: "value2" } h[:key2] #=> value2 ``` Hash literal's keys should always be **symbol literals**. ### Range literal ```ruby (1..10).each do |x| # '..' represents a range puts x*x end ``` ### Boolean and `nil` ```ruby true # Boolean class false # Boolean class nil # Null class !nil #=> true ``` Any objects except `nil` and `false` will be treated as `true` on conditionals. ## Operator ### Arithmetic/logical/assignment operators ```ruby + # unary ** # power - # unary * / % # multiplication, division, modulus + - # addition, subtraction ! # logical inversion > >= < <= # inequality comparison == != # equality comparison, negative comparison && # logical AND || # logical OR += -= # shorthand of addition/subtraction = # assignment ``` *Priority of operators are TBD ### Other operators ```ruby () # changing priority of interpretation [] # array literal * # multiple assignment .. # range ``` *Priority of operators are TBD ### Delimiter ```ruby class Foo; end # ';' to delimit class Bar end # recommended ``` ### String interpolation (to be implemented) ```ruby puts "Error: #{error_message}" # double quotation is required ``` ### Comment ```ruby puts "Goby" # comments ``` Use the annotations to keep the comments concise. - `TODO` - `FIXME` - `OPTIMIZE` - `HACK` - `REVIEW` ### I/O * `#puts` * special constants: `ARGV`, `STDIN`, `STDOUT`, `STDERR`, `ENV` ## Flow control {: .-three-column} ### `if`, `else`, `elsif` ```ruby def foo(str) if str.size > 10 puts "too big!" elsif str.size < 3 puts "too short!" else puts "moderate" end end ``` `then` is **not** supported. ### Break ```ruby def foo(tail) (5..tail).each do |t| if t % 2 == 0 && t % 5 == 0 puts "ouch!" break # finish the block else puts t end end puts "out of the block" end foo 20 #=> 5 6 7 8 9 #=> ouch! #=> out of the block ``` ### Case ```ruby def foo(str) case str when "Elf" puts "You might be Aragorn II!" when "Aragorn" puts "Long time no see, Aragorn!" when "Frodo", "Sam", "Gandalf" puts "One of us!" else puts "You're not yourself" end end ``` ### While ```ruby decr = 10 while decr do if decr < 1 break end puts decr decr -= 1 end ``` `while`, conditional and a `do`/`end` block can be used for a loop. ### Rescue Under construction. Join [#605](https://github.com/goby-lang/goby/issues/605). ## Block {: .-three-column} ### Block ```ruby def foo(ary: [1, 2, 3]) ary.each do |s| # start of the block with |block variable| puts s end # end of the block end ``` `{ }` cannot be used for forming a block. ### `yield` ```ruby def foo yield(10) # executes the block given end foo do |ten| ten + 20 end ``` ### Block object and `call` ```ruby b = Block.new do 100 end b.call #=> 100 ``` `Block.new` can take a block and then `call`. ### Passing a block ```ruby def baz 1000 end class Foo def exec_block(block) block.call end def baz 100 end end b = Block.new do baz end f = Foo.new f.exec_block(b) ``` ### Passing a block with block arguments ```ruby b = Block.new do |arg, offset| arg + 1000 - offset end b.call(49, 500) #=> 549 ``` ### Special `get_block` keyword ```ruby def bar(block) # runs the block object and the block arg simultaneously block.call + get_block.call end def foo bar(get_block) do # passes two blocks to `bar` 20 end end foo do 10 end ``` `get_block` is not a method but a **keyword** to retrieve a given block argument as a block object. By this, you can pass around or `call` the given block arguments as block objects. ### Closure ```ruby count = 0 # the declaration is used b = Block.new do count += 1 # the block looks preserving the `count` end class Foo def bar(blk) count = 9 # (does not affect) puts blk.call # local variable is resolved to the one above end end Foo.new.bar b #=> 1 Foo.new.bar b #=> 2 Foo.new.bar b #=> 3 ``` ## Native class (Primary) {: .-three-column} Goby's most "native" classes cannot instantiate with `new` in principle. ### `Object` ```ruby Bar.ancestors #» [Bar, Foo, Object] Bar.singleton_class.ancestors #» [#, #, Class, Object] ``` `Object` is actually just for creating singleton classes. See `Class`. * **`Object.methods`**: `!`, `!=`, `==`, `block_given?`, `class`, `exit`, `instance_eval`, `instance_variable_get`, `instance_variable_set`, `is_a?`, `methods`, `nil?`, `object_id`, `puts`, `raise`, `require`, `require_relative`, `send`, `singleton_class`, `sleep`, `thread`, `to_s`, `<`, `<=`, `>`, `>=`, `ancestors`, `attr_accessor`, `attr_reader`, `attr_writer`, `extend`, `include`, `name`, `new`, `superclass` * **`Object.new.methods`**: `!`, `!=`, `==`, `block_given?`, `class`, `exit`, `instance_eval`, `instance_variable_get`, `instance_variable_set`, `is_a?`, `methods`, `nil?`, `object_id`, `puts`, `raise`, `require`, `require_relative`, `send`, `singleton_class`, `sleep`, `thread`, `to_s` ### `Class` ```ruby String.ancestors #=> [String, Object] ``` `Class` and `Object`can actually be regarded as the same and you don't need to distinguish them in almost all the cases. * **`Class.methods`**: `<`, `<=`, `>`, `>=`, `ancestors`, `attr_accessor`, `attr_reader`, `attr_writer`, `extend`, `include`, `name`, `new`, `superclass`, `!`, `!=`, `==`, `block_given?`, `class`, `exit`, `instance_eval`, `instance_variable_get`, `instance_variable_set`, `is_a?`, `methods`, `nil?`, `object_id`, `puts`, `raise`, `require`, `require_relative`, `send`, `singleton_class`, `sleep`, `thread`, `to_s` ### `String` ```ruby puts "Hello" + ' ' + 'world' #=> Hello world ``` Fixed to **UTF-8** with mb4 support. * **`String.methods`**: `fmt`, * the rest: `Class.methods` * **`"a".methods`**: `!=`, `*`, `+`, `<`, `<=>`, `==`, `=~`, `>`, `[]`, `[]=`, `capitalize`, `chop`, `concat`, `count`, `delete`, `downcase`, `each_byte`, `each_char`, `each_line`, `empty?`, `end_with?`, `eql?`, `fmt`, `include?`, `insert`, `length`, `ljust`, `match`, `new`, `replace`, `replace_once`, `reverse`, `rjust`, `size`, `slice`, `split`, `start_with`, `strip`, `to_a`, `to_bytes`, `to_d`, `to_f`, `to_i`, `to_s`, `upcase`, * the rest: `Object.new.methods` ### `Integer` ```ruby 37037 * 27 #=> 999999 ``` * **`Integer.methods`**: the same as `Class.methods` * **`1.methods`**: `!=`, `%`, `*`, `**`, `+`, `-`, `/`, `<`, `<=`, `<=>`, `==`, `>`, `>=`, `even?`, `new`, `next`, `odd?`, `pred`, `ptr`, `times`, `to_f`, `to_float32`, `to_float64`, `to_i`, `to_int`, `to_int16`, `to_int32`, `to_int64`, `to_int8`, `to_s`, `to_uint`, `to_uint16`, `to_uint32`, `to_uint64`, `to_uint8` * the rest: `Object.new.methods` ### `Array` ```ruby [1, "2", :card, [4, 5], { john: "doe" }] ``` * **`Array.methods`**: the same as `Class.methods` * **`[1].methods`**: `*`, `+`, `[]`, `[]=`, `any?`, `at`, `clear`, `concat`, `count`, `delete_at`, `dig`, `each`, `each_index`, `empty?`, `first`, `flatten`, `include?`, `join`, `last`, `lazy`, `length`, `map`, `new`, `pop`, `push`, `reduce`, `reverse`, `reverse_each`, `rotate`, `select`, `shift`, `to_enum`, `unshift`, `values_at` * the rest: `Object.new.methods` ### `Hash` ```ruby h = { key: "value" } h = { "key": "value" } #=> error h["key"] #=> value h[:key] #=> value ``` Keys in hash literals should be **symbol literals**, while Hash index can be either string or symbol literals. * **`Hash.methods`**: the same as `Class.methods` * **`{ key: "value" }.methods`**: `[]`, `[]=`, `any?`, `clear`, `default`, `default=`, `delete`, `delete_if`, `dig`, `each`, `each_key`, `each_value`, `empty?`, `eql?`, `fetch`, `fetch_values`, `has_key?`, `has_value?`, `keys`, `length`, `map_values`, `merge`, `new`, `select`, `sorted_keys`, `to_a`, `to_json`, `to_s`, `transform_values`, `values`, `values_at` * the rest: `Object.new.methods` ### `Range` ```ruby (1..10).each do |i| puts i ** 2 end ``` * **`Range.methods`**: the same as `Class.methods` * **`(1..10).methods`**: `!=`, `==`, `bsearch`, `each`, `first`, `include?`, `last`, `lazy`, `map`, `new`, `size`, `step`, `to_a`, `to_enum` * the rest: `Object.new.methods` ### `Block` ```ruby b = Block.new do 100 end b.call #=> 100 ``` * **`Block.methods`**: the same as `Class.methods` * **`(Block.new do end).methods`**: `call` * the rest: `Object.new.methods` ## Native class (secondary) {: .-three-column} ### `Float` ```ruby 1.1 + 1.1 # => -2.2 2.1 * -2.1 # => -4.41 ``` Float literals like `3.14` or `-273.15`. `Float` class is based on Golang's `float64` type. * **`Float.methods`**: the same as `Class.methods` * **`3.14.methods`**: `!=`, `%`, `*`, `**`, `+`, `-`, `/`, `<`, `<=`, `<=>`, `==`, `>`, `>=`, `new`, `ptr`, `to_d`, `to_i` * the rest: `Object.new.methods` ### `Decimal` ```ruby "3.14".to_d # => 3.14 "-0.7238943".to_d # => -0.7238943 "355/113".to_d # => 3.1415929203539823008849557522123893805309734513274336283185840 a = "16.1".to_d b = "1.1".to_d e = "17.2".to_d a + b # => 0.1 a + b == e # => true ('16.1'.to_d + "1.1".to_d).to_s #=> 17.2 ('16.1'.to_f + "1.1".to_f).to_s #=> 17.200000000000003 ``` Experimental: the size is arbitrary and internally a fraction from Golang's `big.Rat` type. Decimal literal is TBD for now and you can get `Decimal` number via `to_d` method from `Integer`/`Float`/`String`. * **`Decimal.methods`**: the same as `Class.methods` * **`(1.1).to_d.methods`**: `!=`, `*`, `**`, `+`, `-`, `/`, `<`, `<=`, `<=>`, `==`, `>`, `>=`, `denominator`, `fraction`, `inverse` * the rest: `Object.new.methods` ### `Regexp` ```ruby a = Regexp.new("orl") a.match?("Hello World") #=> true a.match?("Hello Regexp") #=> false b = Regexp.new("😏") b.match?("🤡 😏 😐") #=> true b.match?("😝 😍 😊") #=> false c = Regexp.new("居(ら(?=れ)|さ(?=せ)|る|ろ|れ(?=[ばる])|よ|(?=な[いかくけそ]|ま[しすせ]|そう|た|て))") c.match?("居られればいいのに") #=> true c.match?("居ずまいを正す") #=> false ``` Using `/ /` is to be implemented. * **`Regexp.methods`**: the same as `Class.methods` * **`Regexp.new("^aa$").methods`**: `==`, `match?` * the rest: `Object.new.methods` ### `MatchData` ```ruby # numbered capture 'abcd'.match(Regexp.new('(b.)')) #=> # # named capture 'abcd'.match(Regexp.new('a(?b)(?c)')) #=> # # converting to hash » 'abcd'.match(Regexp.new('a(?b)(?c)')).to_h #» { 0: "abc", first: "b", second: "c" } ``` The number keys in the captures are actually `String` class.The key `0` is the matched string. * **`MatchData.methods`**: the same as `Class.methods` * **`'abcd'.match(Regexp.new('(b.)')).methods`**: `captures`, `length`, `new`, `to_a`, `to_h` * the rest: `Object.new.methods` ### `File` ```ruby f = File.new("../test_fixtures/file_test/size.gb") f.name #=> "../test_fixtures/file_test/size.gb" ``` * **`File.methods`**: `basename`, `chmod`, `delete`, `exist?`, `extname`, `join` * the rest: `Class.methods` * **`File.new.methods`**: `basename`, `chmod`, `close`, `delete`, `exist?`, `extname`, `join`, `name` * the rest: `Object.new.methods` ## Native class (Golang-oriented) {: .-three-column} ### `GoMap` ```ruby h = { foo: "bar" } m = GoMap.new(h) # to pass values to Golang's code h2 = m.to_hash h2[:foo] #=> "bar" ``` * **`GoMap.methods`**: the same as `Class.methods` * **`GoMap.new.methods`**: `get`, `set`, `to_hash` * the rest: `Object.new.methods` ### `Channel` ```ruby c = Channel.new 1001.times do |i| # i start from 0 to 1000 thread do c.deliver(i) end end r = 0 1001.times do r = r + c.receive end r #=> 500500 ``` `Channel` class is to hold channels to work with `#thread`. See `thread`. * **`Channel.methods`**: the same as `Class.methods` * **`Channel.new.methods`**: `close`, `deliver`, `new`, `receive` * the rest: `Object.new.methods` ## Enumerator & lazy Pretty new experimental library. ### `LazyEnumerator` ```ruby # creating a lazy enumerator enumerator = LazyEnumerator.new(ArrayEnumerator.new([1, 2, 3])) do |value| 2 * value end result = [] enumerator.each do |value| result.push(value) end result #=> [2, 4, 6] ``` A shorthand `#lazy` method is also provided in `Array` and `Range` by now. See "Tips & tricks" below. * **`LazyEnumerator.methods`**: the same as `Class.methods` * **`[1, 2].lazy`**: `each`, `first`, `has_next?`, `initialize`, `map`, `next` * the rest: `Object.new.methods` ### `ArrayEnumerator` ```ruby iterated_values = [] enumerator = ArrayEnumerator.new([1, 2, 4]) while enumerator.has_next? do iterated_values.push(enumerator.next) end iterated_values #=> [1, 2, 4] ``` * **`ArrayEnumerator.methods`**: the same as `Class.methods` * **`ArrayEnumerator.new([1, 2, 3]).methods`**: `has_next?`, `initialize`, `next` * the rest: `Object.new.methods` ### `RangeEnumerator` ```ruby iterated_values = [] enumerator = RangeEnumerator.new((1..4)) while enumerator.has_next? do iterated_values.push(enumerator.next) end iterated_values #=> [1, 2, 3, 4] ``` * **`RangeEnumerator.methods`**: the same as `Class.methods` * **`RangeEnumerator.new(1..2).methods`**: `has_next?`, `initialize`, `next` * the rest: `Object.new.methods` ## Special class ### `Boolean` ```ruby true.class #=> Boolean false.class #=> Boolean ``` A special class that just to hold `true` and `false`. Cannot be instantiate. ### `Null` ```ruby nil.class #=> Null ``` A special class that just to hold `nil`. Cannot be instantiate. ### `Method` (A special dummy class that just holds methods defined by Goby code.) ### `Diggable` Provides `#dig` method. Currently. `Array` and `Hash` classes' instance can be `Diggable`. ```ruby [1, 2].dig(0, 1) #=> TypeError: Expect target to be Diggable, got Integer ``` ## Testing framework ### `Spec` ```ruby require "spec" Spec.describe Spec do it "fails and exit with code 1" do expect(1).to eq(2) end end Spec.run ``` * **`Spec.methods`**: `describe`, `describes`, `instance`, `run` * the rest: `Object.methods` * **`Spec.new.methods`**: `describes`, `initialize`, `run`, `session_successful`, `session_successful=` * the rest: `Hash.new.methods` ## Tips & tricks ### Showing methods ```ruby » "string".methods #» ["!=", "*", "+", "<", "<=>", "==", "=~", ">", "[]", "[]=", "capitalize", "chop", "concat", "count", "delete", "downcase", "each_byte", "each_char", "each_line", "empty?", "end_with?", "eql?", "fmt", "include?", "insert", "length", "ljust", "match", "new", "replace", "replace_once", "reverse", "rjust", "size", "slice", "split", "start_with", "strip", "to_a", "to_bytes", "to_d", "to_f", "to_i", "to_s", "upcase", "!", "block_given?", "class", "exit", "instance_eval", "instance_variable_get", "instance_variable_set", "is_a?", "methods", "nil?", "object_id", "puts", "raise", "require", "require_relative", "send", "singleton_class", "sleep", "thread"] ``` ### Showing class ```ruby » "string".class #» String ``` ### Showing singleton class ```ruby » "moji".singleton_class #» #> » "moji".class.singleton_class #» # ``` ### Showing ancestors ```ruby » Integer.ancestors #» [Integer, Object] » "moji".class.ancestors #» [String, Object] ``` ### Showing singleton classes' ancestors ```ruby » "moji".class.singleton_class.ancestors #» [#, #, Class, Object] ``` ### Showing object's id ```ruby » "moji".object_id #» 842352977920 ``` ### `#to_json` ```ruby h = { a: 1, b: [1, "2", [4, 5, nil]]} h.to_json # converts hash to JSON #=> {"a":1, "b":[1, "2", [4, 5, null]]} ``` ### Customize `#to_json` Overwrite the `#to_json` in your class: ```ruby class JobTitle def initialize(name) @name = name end def to_json { title: @name }.to_json end end class Person def initialize(name, age) @name = name @age = age @job = JobTitle.new("software engineer") end def to_json { name: @name, age: @age, job: @job }.to_json end end stan = Person.new("Stan", 23) h = { person: stan } h.to_json #=> {"person":{"name":"Stan","job":{"title":"software engineer"},"age":23}} ``` ### Lazy enumeration To avoid N + 1 query. ```ruby enumerator = [1, 2, 3].lazy.map do |value| 2 * value end result = [] enumerator.each do |value| result.push(value) end result #=> [2, 4, 6] ``` You can call `#lazy.map` on `Array`, `Range`, or `JSON` objects. ## Styling ### Quick style guide * UTF-8 should be used. * Only two spaces ` ` should be used for one indentation. * Tab cannot be used for indentation. * For more, follow [RuboCop's style guide](https://github.com/bbatsov/ruby-style-guide) in principle. ### Document notation * `Class#instance_method` -- use `#` to represent instance methods in documents * `Class.class_method` * `Module.module_method` ### Syntax highlighting Ready for Vim and Sublime text. You can also use Ruby's syntax highlighting so far. ## References ### Official * Official site: [https://goby-lang.org/](https://goby-lang.org/) * Repository: [https://github.com/goby-lang/goby/](https://github.com/goby-lang/goby/) * DevHints: [https://devhints.io/goby](https://devhints.io/goby) (this page) ### Readings for Goby developers * [Write an Interpreter in Go](https://interpreterbook.com/) * [Nand2Tetris II](https://www.coursera.org/learn/nand2tetris2/home/welcome) * [Ruby under a microscope](http://patshaughnessy.net/ruby-under-a-microscope) * [YARV's instruction table](http://www.atdot.net/yarv/insnstbl.html) ### JP resource * [Goby: Rubyライクな言語(1)Gobyを動かしてみる](https://techracho.bpsinc.jp/hachi8833/2017_11_10/47787) * [Gobyの組み込みクラスにメソッドを追加する方法](https://qiita.com/hanachin_/items/efc1c976a4f5749514ef)