   BRED 3.0.3U;  "Lucida Console",  .
***
  "Programming Crystal" (2019), IVO BALBAERT, SIMON ST. LAURENT.
___________
:
    :
[], [ 4.  (Appendices).],   (),   (    ), - (),  (Companys Story)    .
   :
   (9),     ( ţ ),    .
  & Warning:    ,    "Programming Crystal (2019)",   ,   ..,   .     (     -)     Pragmatic Bookshelf.
---
- (        )      , ݣ        .
---
     :  ,  . .
=============

     .
       Crystal.

---------------------------

		:
  1. .
 1.   Crystal.
 2.  .
   2.   Crystal.
 3.      .
 4.      .
 5.    .
 6.   .
 7.  .
   3.  Crystal-.
 8.   .
 9.  -  .
_________
 4: Appendices.
----------------------

	,    :
	
"# =>"        . :
	p 2 + 2 # => 4
        "p", "puts"  "print"   : 
	2 + 2 # => 4
,     "p", "puts"  "print",    IDE        .

"Error: message"  ,    ;
"Runtime error: message"  ,     .

       ,    $,   . ,  $ crystal build hello_world.cr  ;


    Ruby    Crystal.
  #  .
    ( Shape#perim)  # :  "perim"   Shape.


	,  :
	
 eBook     Programming Crystal (english book).
    Crystal,        Crystal   .
     ;  ,    ,  [$ ruby error1.cr].
    ;  (  )        "".
 ..    ( ,  );
 ..   ;
 .  ;
    , .
    .
    .





						 1. .
						 1.   Crystal.
						
Crystal   "   C    Ruby". Crystal     ,    ,      .  Crystal,   Ruby,   -    ,        .


	_    _
	
   Ary Borenszweig  Crystal  2011 ,       ,         Manas Technology Solutions.     GitHub        ,    250 .      ""     0.27.0.       1.0   ,         Crystal.  ,   Ruby,   ,   ?

      Crystal  ,   Ruby,      Crystal   .      ""     .    Ruby, Crystal       ,   Rust, Go, C#, Python, Julia, Elixir, Erlang,  Swift.        ,       .
Crystal       Ruby    :

   ,       .
   ,     .
       LLVM  ,       .
    ,      Ruby,    .
   : ӣ  Crystal    .
   ,      .
   ,   ,       ,  Fibers (, ).       (    );     CSP    Go.


Crystal   -,  Linux, macOS  FreeBSD   x86/64 (64-),     x86 (32-).     ARMv6/ARMv7.         Windows. Crystal    (  ):     Crystal,       .      :  . ,    ,       ,    .



	_  Ruby,   _

   Ruby   ,     . 
 Hello world"(. hello_world.cr)    :
	puts "Hello, Crystal World!" # => Hello, Crystal World!

      .   ,     ,  'p'    :
	p "Hello, Crystal World!" # => "Hello, Crystal World!"

(     ,    eBook   Crystal   ).             main().   ,   !


Crystal     Ruby:        ,  Ruby    ,     Ruby           .    ,   ,       Crystal,    Ruby,       .        ,       ,     .
          ,     Ruby  Crystal .      1  42      ,     .      .

__why_crystal/fibonacci.cr__
def fib(n)
   return n if n <= 1
   fib(n - 1) + fib(n - 2)
   end sum = 0
   (1..42).each do |i|
   sum += fib(i)
   end

puts sum # => 701408732


  fibonacci.cr,  '.cr'         Crystal. (Ruby  .)     fibonacci.cr,   " " Ruby:    ,   ,    ".each"    .
       : Ubuntu 16.04, 64- ,  AMD A8-6419  12   .

    ,           ,    .     ,  ,         ,    .  ,     Ruby:
$ time ruby fibonacci.cr
   real 3 m 44.437s
   user 3 m 43.848s
   sys 0 m 0.048s

       .       :
$ time crystal fibonacci.cr
   real 0m12.149s
   user 0m12.044s
   sys 0m0.356s
   
Crystal      12 ,    (),    18,5 .
         ,      :
$ crystal build --release fibonacci.cr
    fibonacci,      :
$ time ./fibonacci
   real 0m10.642s
   user 0m10.636s 
   sys 0m0.000s

   ,     21    Ruby!

Crystal    ,       :    (       )   ,    :
$ crystal project.cr

    .   ,        ,   ,   . : Crystal     -  . ,       Ruby  ,       ?
     ,   ,   ,      ,    .

  ,    ,        ,            .   ,         ,       ,  ,  .



	_   Web-_

-     ,   Ruby, Python  PHP.
   Crystal?
 -  Crystal     .  ,  -,   Node.js, Nim, Rust  Scala, HTTP-      ,    .    ,  Crystal    ,   ,          .

     -?     Crystal  ,  Kemal.
    -   RESTful,  
    Ruby.      9 ( " -  
 Kemal  Amber").
Kemal  Sinatra    wrk (     HTTP-      )  -   "Hello-world".
    Crystal:
__why_crystal/kemal.cr__
   require "kemal"
   get "/" do
   "Hello from Kemal!"
   end

   Kemal.run

,     ,    100     30 :
$ wrk -c 100 -d 30  http://localhost:3000

  Kemal     88-93%.
   ,  Kemal   64986        170   ,     Sinatra   2274       43.82 .   ,  Kemal    28     ,  Sinatra. (       Poly Conf 2016: ":     -  ".)



	_   _

Crystal            SQLite, MySQL  PostgreSQL.      ,   Redis,   ,   Redis   () .
 ,        (1000000 .) ,      ,       ,           ,      .

,    ,   ,  .
  c Ruby, Node.js, Java, io.js, Go  C, Crystal   ,     ,   -      Go.

  :     ,  Crystal       :  -,    -,    , ,        .  ,      ?



	_    _

Crystal  ,    .  Crystal      ,    " " (runtime errors).  ,      Crystal,   Ruby, ,   .    "add", ,   ,    :

__why_crystal/error1.cr__
def add(x, y)
   x + y
   end

   puts add(2, 3) # => 5 
   #()      
   puts add(1.0, 3.14) # => 4.14 
   puts add("Hello ", "Crystal") # => "Hello Crystal"
   #+    (.. )

 ,    (+)    ,    ,   ,  :
puts add(42, " times")

    Ruby  : $ ruby error1.cr.

   5, 4, 14  "Hello Crystal",         :
	'+': String can't be coerced into Fixnum (TypeError).

Ruby     ,      . Ruby      ,      .

      Crystal, 
$ crystal error1.cr.
   ;       .  Crystal      ,         :

Error in error1.cr:8: instantiating 'add(Int32, String)'
   add(42, " times")
   ^~~ 
   in error1.cr:2: no overload matches 'Int32#+' with type String


 Crystal       .     .       .    ,      ,    Crystal,  Ruby:

__why_crystal/error2.cr__
  str = "Crystal"
  ix = str.index('z')

  puts str[ix]

     "z"   str,  "Crystal";  ix      .        ,    ?   Ruby,    Crystal, ix   , ,    .


Ruby  ,    :
$ ruby error2.cr
   error2.cr:3:in `[]': no implicit conversion from nil to integer (TypeError)
   from error2.cr:3:in `main'

     ;  ,    ,      ,  .
Crystal,   ,     :
$ crystal error2.cr 
   Error in error2.cr:3: no overload matches 'String#[]' with type (Int32 | Nil)

    ,      (   'String#[]'    ).        ,      .          ,     Ruby:  puts,   String,      .

 :     ix  'str'  ,  'ix'  Nil.

     (    Ruby),  ix  :
__why_crystal/error2.cr__
   if ix
   puts str[ix]
   end 

 if      true    ,  false  "" (   ). ,    if Crystal ,  ix    ,     .
 , Crystal        ,    .
   Ruby, Crystal    ,    ,     ;    .



	_  _

Crystal     ,          .
   ,    , Crystal       ,    ,     .   ,   Crystal   Spec,     -  (  "BDD"); ,        .
        RSpec,  BDD-  Ruby.

  ,  :
           .
  ,        ( "shards":    Crystal,    ).
   (),        , 
   .
   (docs),   Markdown.
   -    ,  Crystal Playground,      (      ).

    , ,   JSON, Crypto, HTTP-  , Web-, OAuth2    .        2200 shards (    Crystal),   .      :       .                Crystal.



	_  Crystal   _

Crystal  C-     ,    Ruby, Python, Java  C#.

    ,    Crystal:
  -,    ,   Ruby.
   Ruby.
  -.
 -  .
     .
     .
      .
   .
     (IoT).

Crystal    ,     Ruby     
.      Ruby,       Crystal,    Go  ++.



		__

 ,         ,      .
         Crystal,    ,
  :
         .
    .
        (fibers).
     C.

      ,           Crystal.







							 2.  .

        .         ,     .   ,          ,        , ,   .     ,   ,       Crystal,           .

  , Crystal        . , ,   ,      Ruby. ,       
,      Ruby      .   ,        ,      ,    Ruby       .

      Ruby,      ,     ,     .   2-              ,  Crystal   ,    .
  Crystal Playground,   .




	_    _

          ,      .       .     ,       =  ;

__foundations/variables.cr__
   name = "Diamond"
   element = 'C'
   hardness = 10

  Crystal    ,      .        .     ,       ,      "typeof".

__foundations/variables.cr__
   puts typeof(name) # => String
   puts typeof(element) # => Char
   puts typeof(hardness) # => Int32

Crystal   "name"  String,      .
  element   (  ),     .
  hardness   ,     32-  ,  10       ( ,      ). (Typeof -   ,        ,     Crystal       .)

         .   
    ,    ,    ,      .

__foundations/variables.cr__
   hardness : Int32
   hardness = 10
   puts typeof(hardness) # => Int32 
   hardness = "20" # => Error... type must be Int32, not (Int32 | String)

    ,       ,     .

 Crystal            .          ,     . 
(     , Crystal      , 
    ,  .)
        ,  hardness : Int32 = 10. (Int32 - 32-     (+-);      .)   ,     Crystal      ,        .
 ,          .

__foundations/variables.cr__
   name, element, hardness = "Diamond", 'C', 10

  ,         ,         .
__foundations/variables.cr __
   name = "Diamond"; element = 'C'; hardness = 10

          ,     .
             :
__crystal_new/variables.cr__
   # swap
   n = 41
   m = 42
   n, m = m, n
   n # => 42
   m # => 41

(  " "   ,    .)
 ,    ,         ,     .

,     ,     ,       (    ).         ,     ,  Crystal  .
__foundations/variables.cr__
   DIAMOND_HARDNESS = 10
   DIAMOND_HARDNESS = 20 # => already initialized constant DIAMOND_HARDNESS

Crystal      ,     .
 (   )     true  false,
  "nil" (   Nil)  ,     .


_______________________________________________
 ...

Ruby   ,     $.  
  ,       .
______________________________________________




_  _

,        ,    -   .

Crystal    ,   ,  
,   :   "float"   . (        ).

__foundations/operations.cr__
   d = 10 + 2 # => 12
   e = 36 - 12 # => 24
   f = 7 * 8 # => 56
   g = 37 / 8 # => 4	;(integer division)
   h = 37 % 8 # => 5	;(integers remainder / mod)
   i = 36.0 / 8 # => 4.5	;(float, or use fdiv function)

    +  ,     .
__foundations/operations.cr__
   "this" + "is" + "a" + "test" # => thisisatest
   "this " + "is " + "a " + "test" # => this is a test

 ,    .     ,    ;   ,    (  )  .
 , Crystal      ,  .

__foundations/operations.cr__
   name = "Diamond"
   hardness = 10
   "The hardness of #{name} is #{hardness}." # => The hardness of Diamond is 10.

Crystal       #{expression},      ,     .
     :     .
Crystal      ,    .   ,  ,      ,   .

, size      String,      Int32.

__foundations/operations.cr__
   name = "Diamond"
   hardness = 10
   name.size # => 7
   hardness.size # => compilation error - undefined method 'size' for Int32

 size      Int32,     (undefined method size on Int32).


  1 ().

        Crystal Playground:
12 + 12
"12 + 12" 
"12" + "12" 
"I" * 5 
'12' + '12' 
5 * "I" 
"12" + 12 
"2" * "5"




	_     _

    ,                . Crystal    ,        .

      ,        .



	 .

    ,        .   Crystal,   . (    ,    )
     :

__foundations/compound_types_arrays.cr__
   minerals = ["alunite", "chromium", "vlasovite"]
   typeof(minerals) # => Array(String) 

    # or, to use a different notation
    minerals2 = %w(alunite chromium vlasovite)
  typeof(minerals2) # => Array(String)

Crystal  ,  minerals     ;   String.
 , Array(String),   ,    .
        <<.  "size"    ,   :

__foundations/compound_types_arrays.cr__
   minerals << "wagnerite"
   minerals << "muscovite"
   minerals
   # => ["alunite", "chromium", "vlasovite", "wagnerite", "muscovite"]
   minerals.size # => 5

Crystal  :      .   :
__foundations/compound_types_arrays.cr__
   minerals << "wagnerite"
   minerals << "muscovite"
   minerals
   # => ["alunite", "chromium", "vlasovite", "wagnerite", "muscovite"]
   minerals.size # => 5

     .    ,      :

__foundations/compound_types_arrays.cr__
   precious_minerals = []
   # => Error: for empty arrays use '[] of ElementType'


  ,                 .       Ruby,      .
   ,        []      "Array"  "new":
__foundations/compound_types_arrays.cr__
   precious_minerals = [] of String
   precious_minerals2 = Array(String).new 

   ,      ,     :
__foundations/compound_types_arrays.cr__
   minerals[0] # => "alunite" 
   minerals[3] # => "wagnerite"
   minerals[-2] # => "wagnerite"
   # negative indices count from the end, which is -1

    ,      .      ,    :
__foundations/compound_types_arrays.cr__
   minerals[2, 3] # => ["vlasovite", "wagnerite", "muscovite"]
   minerals[2..4] # => ["vlasovite", "wagnerite", "muscovite"]

 ,     ?     Crystal    "",       ,    1.     -    ,       (  Crystal  ):
__foundations/compound_types_arrays.cr__
   minerals[7] # => Runtime error: Index out of bounds (IndexError)


    ,      () ,     .   []?,   "nil"   :

__foundations/compound_types_arrays.cr__
   minerals[7]? # => nil

  1  ,     "nil",     .   ,       .
,          ?

__foundations/compound_types_arrays.cr__
   pseudo_minerals = ["alunite", 'C', 42]

 ,      :
__foundations/compound_types_arrays.cr__
   typeof(pseudo_minerals) # => Array(Char | Int32 | String)

  ,      Char,  Int32,  String.  ,       "Char | Int32 | String". (       ,        ,    .)


 ( )      Crystal:          ,    ,         .    ,        ,    .

 "includes?"   ,      . 
  arr = [56, 123, 5, 42, 108]
  arr.includes? 42 # => true

     ?  "shift"  "pop"  .
   p arr.shift # => 56
   p arr # => [123, 5, 42, 108]
   p arr.pop # => 108
   p arr # => [123, 5, 42]

       ,      "each"      .
arr.each do |i|
   puts i 
   end

API   "Array"     .  ,      ,     " " (  )      .    ,  "StaticArray",     ,      .

_________________________________________
 .

  ?    :
	arr = [1, 'a', "Crystal", 3.14]
   print arr # [1, 'a', "Crystal", 3.14] (no newline)
   puts arr # [1, 'a', "Crystal", 3.14]
   p arr # [1, 'a', "Crystal", 3.14]
   pp arr # [1, 'a', "Crystal", 3.14]
   p arr.inspect # "[1, 'a', \"Crystal\", 3.14]"
   printf("%s", arr[1]) # a (no newline)
   p sprintf("%s", arr[1]) # "a"


"pp"  "inspect"   ; printf  sprintf    ,   C;   (sprintf)   String.
_______________________________________



  2 ().

  :
         ,       .  Crystal     ,    .

    API Crystal  ,    ["", "", ""]  ["", ""],        (..   ).

minerals = ["alunite", "chromium", "vlasovite"]
minerals.delete("chromium")

p minerals #=> ["alunite", "vlasovite"]





 .

 ,     ,      "",       ,    .      .

  ,         ,    .
  ,      .    (    )  :
__foundations/compound_types_hashes.cr__
   mohs = {  "talc" => 1,
   "calcite" => 3, 
   "apatite" => 5, 
   "corundum" => 9, 
   } 
   typeof(mohs) # => Hash(String, Int32)


 , Hash (String, Int32),     (String)    (Int32).   
    ,   :
__foundations/compound_types_hashes.cr__
   mohs["apatite"] # => 5

,     ,  gold ??
,      ,     :
__foundations/compound_types_hashes.cr__
   mohs["gold"]
   # => Runtime error: Missing hash key: "gold" (KeyError)


 ,    ,       ,    ,       []?,   (nil):

__foundations/compound_types_hashes.cr__
   mohs["gold"]? # => nil
   
,  ,      "has_key?":
__foundations/compound_types_hashes.cr__
   mohs.has_key? "gold" # => false


   -      .       "" ,    ,      :

__foundations/compound_types_hashes.cr__
   mohs["diamond"] = 9 # adding key
   mohs["diamond"] = 10 # changing value
    mohs
   # => {"talc" => 1, "calcite" => 3, "apatite" => 5,
   # "corundum" => 9, "diamond" => 10} 
   mohs.size # => 5 

,      4  5.     (+),            ?

__foundations/compound_types_hashes.cr__
   mohs['C'] = 4.5 # Error: no overload matches 
   # 'Hash(String, Int32)#[]=' with types Char, Float64

      : Crystal    !
,       ?

__foundations/compound_types_hashes.cr__
   mohs = {} # Error: Syntax error: for empty hashes use
   # '{} of KeyType => ValueType' 

  .      ,    :
__foundations/compound_types_hashes.cr__
   mohs = {} of String => Int32 # {}
   mohs = Hash(String, Int32).new

    ,        Hash,        API.



  3 ().

  ? Crystal     ,       ,   .         ,     .   API              ,     .

mohs = {
   "talc" => 1, 
   "calcite" => 3,
   "apatite" => 5,
   "corundum" => 9
 } of String => Int32

   p mohs.empty? => false





	_ :   _

,      ,       .  
 , Crystal   ,     ,         .

          ݣ     3      .


 .

Crystal    if  ,      . A   if      -,    :

__foundations/control_flow.cr__
   hardness = 7 # quartz
 
   if hardness < 8
   puts "softer than topaz"
   end
   # => softer than topaz

   7  8,  Crystal   " ".         8,     ,   if    .

Crystal     if,   elsif  else,     ""   . (   ""          if.)

__foundations/control_flow.cr__
   hardness = 5.25
   if 0 < hardness < 5
   puts "softer than apatite"
   elsif hardness < 8
   puts "harder than apatite, softer than topaz"

 else 
   puts "topaz or harder!"
   end
   # => harder than apatite, softer than topaz


        if   . ,  "elsif",
 .  , "else",  ,        ,      .
 Crystal  ,  ,    ,    "
 "    . Crystal     "",  "false"  "nil" (  )  ,  ӣ   "true"; , , ,       .

   if  ,         if    .

__foundations/control_flow.cr__
   output = if 0 < hardness < 5
   "softer than apatite"
   elsif hardness < 8
   "harder than apatite, softer than topaz"
   else
   "topaz or harder!"
   end
   output # => harder than apatite, softer than topaz


     if   ,     .

__foundations/control_flow.cr__
   output = "softer than topaz" if hardness < 8 # => softer than topaz

   ,   ,     ,   "unless",  :
__foundations/control_flow.cr__
   output = "softer than topaz" unless hardness >= 8
   output # => softer than topaz

  if       ,         .

    -     ,  "case-when"   ,     .

__foundations/control_flow.cr__
   output = case hardness
   when 4
   "hard as fluorite"
   when 7
   "hard as quartz"
   when 10
   "hard as diamond"
   else
   "can't say how hard"
   end # => "can't say how hard"

    "case-when",    ,   ,    if.

__foundations/control_flow.cr__
   output = case 
   when 0 < hardness < 5
   "softer than apatite"
   when hardness < 8
   "harder than apatite, softer than topaz"
   else
   "topaz or harder!"
   end # => harder than apatite, softer than topaz"



.

        .      times   Int.          "do ... end" ,       ;     {;;},       .

__foundations/control_flow.cr__
   # Int#times 
   5.times do
   p "Hi"
   p "Low"
   end
   # same as:
   5.times { p "Hi"; p "Low" }


 ,  :  times    ,   C,       .          ,  ݣ    .

    ݣ   , Range.   ,    2..7 (.  ),         (start..end).
        . (   ...  
 ,   .)        , ,   .      ,         "to_a".

__foundations/control_flow.cr__
   inc = 2..7
   p inc.to_a #=> [2, 3, 4, 5, 6, 7]
   exc = 2...7
   p exc.to_a #=> [2, 3, 4, 5, 6]


   "each",    -     .    , ,      .

__foundations/control_flow.cr__
   # Range#each
   mohs_list = ["nothing", "talc", "gypsum", "calcite", "fluorite", "apatite",
   "orthoclase feldspar", "quartz", "topaz", "corundum", "diamond"]
   (2..5).each do |i|
   puts mohs_list[i]
   end 
   # produces:
   # gypsum
   # calcite
   # fluorite
   # apatite

       , ,    ,    "each"   .

__foundations/control_flow.cr__
   # Range#each
   mohs_list = ["nothing", "talc", "gypsum", "calcite", "fluorite", "apatite",
   "orthoclase feldspar", "quartz", "topaz", "corundum", "diamond"]
   mohs_list.each do |mineral|
   puts mineral
   end
   # produces:
   # nothing
   # talc
   # gypsum
   # calcite
   # fluorite
   # apatite
   # orthoclase feldspar
   # quartz
   # topaz
   # corundum
   # diamond


       , Crystal     "loop do ... end".     ,         (break):

__foundations/control_flow.cr__
   n = 1
   loop do
   puts "a mighty crystal"
   n += 1
   break if n == 3
   end
   # => a mighty crystal
   # => a mighty crystal


Crystal     while,  ""    ,       (false):

__foundations/control_flow.cr__
   a = 1
   while (a += 1) < 10
   if a == 3
   next
   elsif a > 6
   break
   end
   puts a
   end # => 2, 4, 5 and 6 on successive lines

     , "next"      (..   ),   break    (    7).

 ,    ,     ,       ,  "until condition"  "while !condition".

__________________________________________
   .

  Crystal             C,   Ruby,      Chars   .
  ,        Crystal:    
,  "".
_________________________________________


  4.
  Crystal,  while  if,       [https://en.wikipedia.org/wiki/99_Bottles_of_Beer].
  :     "Int32#downto".





	_ _

      ,   ""     . Crystal,   Ruby,      ,   .   ,   (  Crystal   !)   .     ,   Crystal        ,       .

     - typeof, puts, p -    ,   .      .

 "size"    ,   "each"   . ,      ,       ,      ,   "def"  "end",      Ruby.

__foundations/methods.cr__
   def double(num)
   num * 2
   end
   
   puts double(6) # => 12

       .      ;        .    ,     .  ,       .     "returns num * 2",     .

 double  ,        ,   ,
        .        ; double("6")   66.  double(true)  :
in line 2: undefined method * for Bool.

      ,        ,     :

__foundations/methods.cr__
   def double(num : Int32)
   num * 2
   end
   
   puts double(6) # => 12


,    double("6"), Crystal  "",      double   String. , ""     .

Crystal    ,               (  ) .          ,  :

__foundations/methods.cr__
   def double(num : Int32)
   num * 2
   end
   
   def double(str : String)
   str + " " + str
   end
  
    puts double("6") # => 6 6
   puts double(6) # => 12

       ,   ,       .


  5.
  sample,   ,       .        . (:  rand    ,   float.)




	_     _

     ,         ,    .  Crystal   - ,        .          ,          "new".    ,      ,        .        Ruby,  Crystal  Σ  .


  .

     ,          ,  ӣ    .      ,            (       ),       .

    ,     .        ,        .
__foundations/classes.cr__
   class Mineral 
 
   end 
   mine = Mineral.new() 
   puts typeof(mine) # => Mineral 

    ,      .         , -  mine = Mineral.new("talc", 1.0).      ,         ( Float64).
 ,    Ruby,      initialize (   ),    .

__foundations/classes.cr__
   class Mineral
   def initialize(common_name : String, hardness : Float64)
   @common_name = common_name
   @hardness = hardness
   end
   end
   mine = Mineral.new("talc", 1.0)
   puts typeof(mine) # => Mineral


  "common_name"  "hardness"     (  ),
   ,     ,   ,     .
"@common_name"  "@hardness"   ,     . Crystal         initialize.       (  @)    ,  Crystal       .

__foundations/classes.cr__
   class Mineral
   def initialize(@common_name : String, @hardness : Float64)
   end
   end 
   mine = Mineral.new("talc", 1.0)
   puts typeof(mine) # => Mineral

            .   , , Mineral.common_name,      common_name  Mineral.class.
     "common_name=",   @common_name,  Crystal  -  : ""  "" (getters  setters).

      "common_name"  ,     (hardness), :

__foundations/classes.cr__
   class Mineral 
   getter common_name : String
   setter common_name
   getter hardness : Float64
   
   def initialize(common_name, hardness)
   @common_name = common_name
   @hardness = hardness
   end
   end
   mine = Mineral.new("talc", 1.0)
   puts mine.common_name # => talc
   mine.common_name="gold"
   puts mine.common_name # => gold
   puts mine.hardness # => 1.0


    ,       (  ),           ,      getter   .
         ,    .        ,   .      (hardness),  ӣ    "undefined method name" -   "setter". (  ,   ,  getter  "attr_reader"   Ruby,  setter  "attr_writer",  "property"  "attr_accessor".      .)

   ,    Crystal     .    ,    ,             .       :

__foundations/classes.cr__
   class Mineral 
   getter common_name : String

   setter common_name
   getter hardness : Float64
   getter crystal_struct : String

   def initialize(@common_name, @hardness, @crystal_struct)
   end 

   def describe
   "This is #{common_name} with a Mohs hardness of #{hardness}
   and a structure of #{crystal_struct}."
   end
   end
   mine = Mineral.new("talc", 1.0, "monoclinic")
   puts mine.describe # => This is talc with a Mohs hardness of 1.0
   # => and a structure of monoclinic. 


   describe,     crystal_struct.   (describe)         ,  .
  ,     "",       (      .)   II,  5 (   ),       , ,    .


  6.
1) ,        Mineral  ,   (crystal_struct)     .    ? :        (property).

2)   to_s,    (String)   Mineral,      . (:    self.)



 .

    ,    . ,  Random          .    ""     ;      mixin.  ,      .
     Hardness,    hardness,      :
__foundations/modules.cr__
   module Hardness
   def data
   {"talc" => 1, "calcite" => 3, "apatite" => 5, "corundum" => 9}
   end
  
   def hardness
   data[self.name]
   end
 end

     Mineral       name,      " "  Hardness:
__foundations/modules.cr__
   class Mineral 
   include Hardness
   getter name : String

   def initialize(@name)
   end
   end

  ,         Mineral:

__foundations/modules.cr__
   min = Mineral.new("corundum")
   min.hardness # => 9

    (extend) ,        "".
_________________________________________
	:
   extend  include  :
*       ;      .
*            ,    "include ..." ; ,    .
________________________________________





	_     _

          ,    
 ,    .        .
          ,           .

 ,  ,        .  Ruby         Thread.new,       .      .  ,   -, Ruby  GIL (  ),  ,     Ruby   ,       .
  , Crystal        ;       (eBook)        .


  ""   Crystal    :
1) ,     ,   "spawn"    .
2) ,       .
     ,   ,   ,     ,   .   ,      ,     .

 ,  Ruby   1.9   ,  Crystal    ,    .
  ,     .  ,  10 000 ,         "fiber #{i}: I like crystals!"  ,    ,     .             :

__foundations/fibers.cr__
   chan = Channel(String).new 
   num = 10000
   num.times do |i|
   spawn do
   chan.send "fiber #{i}: I like crystals!"
   end
   puts chan.receive
   end 

   # => 

  # fiber 0: I like crystals!
  # fiber 1: I like crystals!
  # fiber 2: I like crystals! 
  # ... 
  # fiber 9999: I like crystals!


 , ,     ,    .       ""  ""  Crystal:
 concurrent ,         ( 
    ),   "" (..  )    ;
  (parallel),   , ,        ,     (  );
  0.26.1      .  ,    
,  Crystal     ,          .
     ,       " ".

     ݣ      "   ".


  7.
  ,  ,    500 000 .


		__
      ,       .         ,    "" ,     .      ,   ,     Ruby    Crystal.        .     ,          .

  II         ,  ,  Crystal    ,      .







							 2.
							  Crystal.
	
        Crystal:          ,      ,   , , ,   (   ,  ""   ).


					 3.
					     .

,   ,   Crystal,   ,    ,  
   ,             .

  .      ,   ,   .    ,   Ruby  Python, ,  ,      .       ,        .
Crystal   ,   ,     ,    .   Crystal     ,     
.              .

 ,     ,      
 .   ,   ,        ,          .      ,  ; ,       .

Crystal     .      "", 
       : ,   () ,    .      ,     "Bool | Int32".
         .   ,  ,       (..  , command-line).    ,   "",      ,   Crystal.

    ,     ,   .     :      .    
,           
  .            ,    ,   .



	_  _

         Crystal,    .  -  ,       .           Ruby: Crystal       ,       .     ,       
  . ( Crystal      , , int8  int32.)

   :           ,  , 1 USD -  64.34603985   (INR).           ,      . ,     ,       .
          ,   ,        .

 ,     ,   ,       "to_i":
"64.34603985".to_i # Runtime error: Invalid Int32: 64.34603985 (ArgumentError)

      '64',  Crystal  ""   .        ,   ,         "to_".
 ,  ,       :
"64.34603985".to_f # => 64.34603985

     "float"   ,   to_i,    "" 
:
  rate = 64.34603985 
   rate.to_i # => 64

     ?

rate1 = 64.34603985
rate2 = "7"
  rate1 + rate2 # Error: no overload matches 'Float64#+' with type String


    ,    to_i  rate2;  to_s  ӣ   String,
   rate1  rate2    .    ,        ,   :

  rate1 + rate2.to_i # => 71.34603985
  rate1.to_s + rate2 # => "64.346039857"

, ,  ,        ,    'to_f ':

  rate1.to_f # => 64.34603985
  rate2.to_f # => 7.0
,  ,    ,     :
  curr = "Crystal"
  curr.to_i # => Runtime error: Invalid Int32: Crystal (ArgumentError) 

Crystal         ,     .   ,       (A | B),   ,  
  B,    "as(B)",        'B'.
     ,       'to_',  .
     ,    ,         .


  1.

    : Crystal    ,       to_i    -    . Crystal ,  ,       8-  64-,   ,    . 
     ?  ,       ,     ң ,   .  Crystal    ,          ,      .  -,        :

p int8 = 1_i8 # 8-bit signed integer 
   p int16 = 16_i16 # 16-bit signed integer
   p int32 = 132_i32 # 32-bit signed integer
   p int64 = 164_i64 # 64-bit signed integer
   p uns64 = 264_u64 # 64-bit unsigned integer


1) p int64 + int32 + uns64=	;
   
2) p int8 + int64=	;




	_  _

         ,    , 
            .  ,           .        ;   ,      ,    .

  ,  Crystal   ,          ,       .       .        "getting_input.cr".

  ""  ,   Playground  .   ,     ()  :
crystal getting_input.cr.

,   ,   ,     256 (    
  'Int8').

puts "Enter the numbers one by one, and end with an empty line:"
  ## "    ,     :"
arr = [] of Int8


    ,       "[] of Int8".    ,     , ,     :

arr1 = [75, 42, 126]
   typeof(arr1) # => Array(Int32)

    'Int32',      127? Int32 
 ,   .      Int8, ,    ,      ,  :

arr1 = [75_i8, 42_i8, 126_i8]
   typeof(arr1) # => Array(Int8)

(Crystal   i8, i16, i32, i64      ,  u8, u16, u32  u64    .)       gets,   ,   ,   String.       ,   "".

puts "Enter a number:"
   num = gets 
   p "You entered #{num}" # => "You entered 42"

								@@@ErrOR=arr
     :
 arr1 << num

Crystal     :
Error: no overload matches 'Array(Int8)#<<' with type (String | Nil)

       .   ,    ,    "num" (     ).      :

p typeof(num) # => (String | Nil)
   p num.class # => String

 ,  Crystal     :
    :    ,     "typeof";
   :    ,    ;   
 "class".

 , nil        .    ,      (nil)?
 ,     "CTRL+D"  ,  :
   num   Nil!   '<<'       (nil).     "" .

    .  nil  "",       if:

if num
   arr << num
   end


   ,        :
 no overload matches 'Array(Int8)#<<' with type String 

  ,  num   (String) ,      Int8.        to_i8:

if num
   arr << num.to_i8
   p arr # => for example: [42]
   end

 Crystal    8-     (+-).

     (..  ),       .      ,         -127  128. 

    ,         .  , 
if     .   ,    ,    .  while  : gets   ,       while.     ""  "" (false),  while      "num"  .

while num = gets
   arr << num.to_i8
p arr # => for example: [2, 3, 3, 5]
end

,  nil, false      Crystal         if, unless, while  until.       "false"   ''       .

___________________________________________
 .

   \  "a ||= b",     
a || (a = b),    'b'  'a'    ,  'a'  :
mem = nil
   mem ||= 1
   mem # 1
   mem ||= "Crystal"
   mem # 1

    "memoization",     (  )  ;      'a',  'a'   ,      b.
_________________________________________

   ,        strip,   .   , ,    "stop"   ENTER,     (break):

while num = gets
   num = num.strip # removes whitespace
   if num == "" || num == "stop"
   break
   end
   arr << num.to_i8
   end
   p arr # => for example: [78_i8, 56_i8, 12_i8]


_______________________________
read_line.

   gets ,  ,    (  nil),   ,      "read_line".     read_line.cr ,   .
______________________________



	_      ( 1)_

      .      ,          , :
42   (USD) = 277.86846192   (CNY).
  ?
 ,   "CNY"  "USD",  ,  (String).      ,  " "  " ".             Hash(String, String),  :

CURRENCIES = {
   "EUR" => "Euro",
   "CAD" => "Canadian Dollar",
   "CNY" => "Chinese Yuan"
   }

      base.   (   )    .   ,       ,     Float64.  ,   (rates),     
 ,    ,   Hash(String, Float64).       (   "USD"  ),          ţ  .
  :

__types_and_control_flow/curr_conv1.cr__

   puts "Enter the base currency, default is: USD"
   base = gets
1. exit unless base
   if base.strip == ""
   base = "USD" # take "USD" as default value
   end
   puts "Enter the other currencies and their exchange rate,"
   puts "like this: EUR - 0.84320536"
   puts "Stop input by entering 'stop'"
   rates = Hash(String, Float64).new
   while input = gets
2. break unless input
   input = input.strip
   if input == "" || input == "stop"
   puts "No more input: ok, we stop"
   break
   end
3. if !input.includes?(" - ")
   puts("Wrong input format, use: CUR - 1.23456")
   break
   end
4. arr = input.split(" - ")
   curr = arr[0].upcase
   if curr.size != 3
   puts "Currency code must be 3 characters"
   break
   end
   rate = arr[1] 
5. rates[curr] = rate.to_f
   end
   puts "base: #{base}"
   puts "rates: #{rates}"


       :

<=   Enter the base currency, default is: USD
=>   CAD
<=   Enter the other currencies and their exchange rate, like this:
   EUR - 0.84320536
   Stop input by entering 'stop'
=>   EUR - 0.84
=>   INR - 64.34
=>   stop
<=   No more input: ok, we stop
   base: "CAD"
   rates: {"EUR" => 0.84, "INR" => 64.34}


1.   ,  'base'   'nil' (),        .
    ,    : "undefined method strip for Nil"   .

2.  ""  ,       (..  ).

3.   .     " - "()?

4.  "Split"  ,               .
    ,        (3)      .
  :   each_char   "String"   "ascii_letter?"   Char. ,   ""    .


5.       ,  .     ||=;
        .


      "String",   "rates"       Float64,     .  "to_f"   :
"no overload matches Hash(String, Float64)#[]= with types String, String"

,  ,     ,    .




	_    _


,    ""   ,         . , ,   " ,  ":       .    , ,    , 
 "abc",     ;  , ,        .   ?
- :        ().    ,      .

<=   Invalid Int8: abc (ArgumentError)
0x451477: *CallStack::unwind:Array(Pointer(Void)) at ??
   0x465a92: to_i8 at /opt/crystal/src/string.cr 358:5
   ...

     .          "to_i8?",  ,      .  ,     (nil):

if num.to_i8?
   arr << num.to_i8
   else
   puts "Input is not an integer."
   end


      ,      ! (   Ruby,  Crystal  ,    ,   "?",   (nil),    .)   ,           ,  "raise":

 if num.to_i8? 
   arr << num.to_i8
   else
   raise "Input is not an integer."
   end 

         :

__types_and_control_flow/curr_conv2.cr__
   if rate.to_f?
   rates[curr] = rate.to_f
   else
   puts "rate is no decimal number"
   end 

  "puts",   .    begin-rescue,     :

__types_and_control_flow/getting_input_exception.cr__
   puts "Enter the numbers one by one, and end with an empty line:"
arr = [] of Int8
   while number = gets
   number = number.strip # removes leading or trailing whitespace
   if number == "" || number == "stop"
   break
   end
   begin
   arr << number.to_i8
   rescue
   puts "integer bigger than 255"
   end
   end
   p arr # => for example: [78, 56, 12]

 rescue     ,       .
  ,  ,      ,     ,    Java  Ruby.         ,   "to_i?"   "to_f?",  .
     begin-rescue,     begin  rescue,      ,    .      ,     !


   Ruby,       "else" and "ensure".   "rescue"      "ex":

begin
   # some dangerous code here
   # possibly your own raise "..."
  rescue ex
   # execute if an exception is raised
  p ex.message
  else
   # execute if an exception isn't raised
  ensure
   # this will always be executed
  puts "Cleanup..."
end



	_ _

-,               .       .     Crystal  ,             .       ,    ,         - . 
   :

__types_and_control_flow/chaining.cr__
 result = (42..47).to_a # => [42, 43, 44, 45, 46, 47]		(1)
.sort { |n, m| m <=> n } # => [47, 46, 45, 44, 43, 42]		(2)
.reject { |n| n.odd? } # => [46, 44, 42]			(3)
.map { |n| n * n } # => [2116, 1936, 1764]			(4)
.select { |n| n % 4 == 0 } # => [2116, 1936, 1764]		(5)
.tap { |arr| puts "#{arr.inspect}" } # => [2116, 1936, 1764]	(6)
.sort! # => [1764, 1936, 2116]					(7)
.any? { |num| num > 2000 } # => true				(8)


1)      "to_a".
2)      (< = >   ).
3)       "reject".
4)  "map"      ,      .
5)   ,   4.
6)  "tap"               .
7)   .
8)   "any?"      2000.


      "reduce",        ,       (sum)    arr:
__types_and_control_flow/chaining.cr__
   sum = (42..47).to_a
   .reduce(0) { |sum, num| sum + num }
   # => 267 (= 42 + 43 +... + 47)

 reduce      ,   Crystal.
        ,    Ruby.
 ,    "!" ( sort!)    ,    . (  "pp!"    ,  "pp".)
 ,    "?" (  "any?")      "true"  "false".




	_      _

     .     Crystal        .  ,           ? ,              (1 USD = 0.8432053 EUR):
	$ crystal argv.cr USD EUR 0.84320536

Crystal,   Ruby,   ARGV  ,     :

__types_and_control_flow/argv.cr__
   puts "Number of command line arguments: #{ARGV.size}" # => (1)
   ARGV.each_with_index do |arg, i|
   puts "Argument #{i}: #{arg}" # => (2)
   end
   p ARGV # => (3)
   p "Executable name: #{PROGRAM_NAME}" # => (4)
   p "Path to source file: #{__FILE__}" # => (5)
   p "Folder of source file: #{__DIR__}" # => (6)


# (1)    : 3
# (2)
	#  0: USD
	#  1: EUR
	#  2: 0.84320536
# # (3) ["USD", "EUR", "0.84320536"]
# (4) "   :/$ HOME/.cache/crystal/crystal-run-argv.tmp"
#  (4) "  : ./argv"

# (5) "   :
# / $HOME/crystal/Book/code/types_and_control_flow/argv.cr
# (6) "  :

#/$HOME/crystal/Book/code/types_and_control_flow"


   ARGV    "each"  "each_with_index",   do-block  ,      .    :     db_json,  :
	$ ./db_json sqlite3:/ / db/sqlite3.db
,  ARGV[0]      .

          PROGRAM_NAME.   "./argv",         ():
$ crystal build program.cr.

      - 
	$ crystal program.cr
,  ,        .cache/crystal.       (   ,  ""   ".o",    Playground   ).

______________________________________
"__FILE__"  "__DIR__".

__FILE__    ,       .  __DIR__    .
_______________________________________


    ,         ,   ,   $ ./curr_conv3 EUR:

__types_and_control_flow/curr_conv3.cr__
   base = ARGV[0]?
   base = "USD" unless base




	_  _

   ""   ,         .      "  "  .   Ruby,  Crystal     .         ,             .

Crystal,   Ruby,  - () :
  str = "What a beautiful mineral!"
  str1 = "What a
 beautiful mineral!" # multi-line string

Crystal      ,       .        "str1",   ,       ,    :

  str = "What a beautiful mineral!"
  str1 = "What a \nbeautiful mineral!" # multi-line string

 ,      (\")    , "" (\\).      .  "\u"         .  ,\u2603   .

      , Crystal      :

__types_and_control_flow/strings.cr__
   curr1 = "US Dollar"
   curr1[2..4] # => " Do"
   curr1.reverse # => "ralloD SU"
   curr1.size # => 9 # length or len do not exist
   curr1.upcase # => "US DOLLAR"
   curr1.capitalize # => "Us dollar"
   curr1.includes? "la" # => true
   curr1.count "l" # => 2 
   curr1.starts_with? "Us" # => false # case sensitive!
   curr1.ends_with? "ar" # => true
   curr1.index("a") # => 7
   curr1.sub("ll", "l") # => "US Dolar"
   curr1.gsub(/([aeiou])/, "*\\1*") # => "US D*o*ll*a*r"
   curr2 = curr1.split("") # => ["U","S"," ","D","o","l","l","a","r"]
   curr2.join("-") # => "U-S- -D-o-l-l-a-r"

 split  join    :
split                  join
  ,    .

     "" (   ).  ,   ,        :
   s = "USD"
   s[2] = 's' # => Error: undefined method '[]=' for String


          .    to_s, "+"        ,    :

rate = 0.84320536 
   p "rate: " + rate.to_s # => "rate: 0.84320536"
   # string interpolation is more efficient:
   p "rate: #{rate}" # => "rate: 0.84320536"

        "<<":
   str = String.build do |io|
   io << "rate: " << rate
   end 
   p str # => "rate: 0.84320536"

         ,        .


  2.

1)       ,      :
gold;topaz;apatite;wolframite;calcite;diamond. (; ; ; ; ; .)
(:   "map",       " ").

2)         UTF-8.    ,    "hi //"(.    .eBook).
         , " " (. codepoint      )  .          [https://crystal-lang.org/api/master/String.html].          ,      ?

________________________________________________________
  "each"  .

  "each"  :      "while"         [].
_______________________________________________________

3) object_id	;
,                "". (:   object_id.)   Crystal    ?
    ޣ:   ,      object_id,  "=="   "true",    "same?".  ,    object_id     (Boolean) ?




	_    _

,      ,  ,      . ,              ,    .      ,          :

__types_and_control_flow/symbols.cr__
   class Mineral
   property name
   
   def initialize(@name : Symbol)
   end
   end

   mineral1 = Mineral.new(:talc)
   mineral8547 = Mineral.new(:talc)

   p mineral1.name
   # => :talc
   p mineral8547.name # => :talc
   p :talc # => :talc

 mineral8547.name = :gold
   :gold.class # => Symbol


      Int32,         .       -,     (Strings).

         :

__types_and_control_flow/curr_conv3.cr__
   CURRENCIES = {
   :EUR => "Euro", 
   :CAD => "Canadian Dollar",
   :CNY => "Chinese Yuan",
   :INR => "Indian Rupee",
   :MXN => "Mexican Peso",
   }

   ,       "?"  "!".
      , : "*"  "&".


  3.
      ,    ( ) ?
 .




	_ _

        ,     .
           ,       ,   .
Crystal   (Enums)    ,  ,       :

__types_and_control_flow/enums.cr__
   enum Direction

   North # value 0
   East # value 1
   South # value 2
   West # value 3
   end

   Direction::South # South
   Direction::South.value # => 2


      ,            .    Crystal       Enum,        .    ݣ   :   .      ,      .

Direction::Eest # Error: undefined constant Direction::Eest
   :gold 
   :goold

        ,        Ruby.



	_  _


   ,        .           .   ("" --  . regex)       ,     .

Crystal   PCRE.      C- PCRE. ( Ruby    regex,     PCRE.)        /pattern/.
    ,    (""),    ,       "%r(pattern)".         .

  "=~"   match,  ,         .

__types_and_control_flow/regex.cr__
   str = "carbonantimonygolddiamond"
   pattern = /gold/
   p str =~ pattern # => 14

   gold     14.   "gold"   ,   (match)   (nil).     " ",        "if materials =~pattern",    ,   ,  -  ,   .

              :

__types_and_control_flow/regex.cr__
   materials = "carbonantimonygolddiamond"
   pat = /(.+)gold(.+)/ # searches for gold

   str = "carbonantimonygolddiamond"
   str =~ pat # => 0
   $1 # => "carbonantimony"
   $2 # => "diamond"

   str.match pat # => 
   # <Regex::MatchData "carbonantimonygolddiamond"
   # 1:"carbonantimony" 2:"diamond">


       "" (  ,      "if"),     (.+).    Regex       .

Crystal      (#{})    ,         . ,         ,     "" ,    !




_      ( 2)_

              XML-,   -.       :
<title> 1 USD = 0,74402487 GBP </title>
         "USD"    "GBP",       0.74402487.      :

__types_and_control_flow/curr_conv4.cr__
  base_currency = "USD"
   currency = "GBP"
   line = "<title>1 USD = 0.74402487 GBP</title>" # exchange rate format

   regex = {
   :open => /<title>1 #{base_currency} = /,
   :close => / #{currency}<\/title>/ 
   }

   rate = line.gsub(regex[:open], "").gsub(regex[:close], "").to_f
   # => 0.74402487

   /<title\>1 #{base_currency} = /
        "line" (    ).
    /#{currency} <\/title\>/
  .    "gsub"     ,         "to_f",  .



	_   :   _

 ,     ,       ,     . Crystal        ,     .       (  )  .   ,    {.},    Tuple.new:

  tpl = {42, "silver", 'C'}
  tpl.class # => Tuple(Int32, String, Char)
   a = Tuple.new(42, "silver", 'C')

  ,    ,  "n, m = 42, 43",    .         :

 tpl[0] # => 42 (Int32)
   tpl[1] # => "silver" (String)
   tpl[2] # => 'C' (Char)
   var = 89

 tpl[var] # => Index out of bounds (IndexError)
   tpl[var]? # => nil

 ,    ,             ;       (IndexError)   .    ,     "[]?",      .        Tuple,   "size, each, includes?, map"   .
    ,     ,      "" ,     .

  "||=" (,       "  ")      :
 h = {1 => 'A'}
 h[3] ||= 'C'
 h # => {1 => 'A', 3 => 'C'}

            :
  rates[curr] ||= rate.to_f
 ,       ;     .

        ,  Crystal    ,     ,    :

 tpl = {name: "Crystal", year: 2017} # NamedTuple(name: String, year: Int32)
  tpl[:name] # => "Crystal" (String)

       ,   .         .     ,     ,       ,        .

         :

# Instead of {:key1 => 'a', :key2 => 'b'} you can use:
   {key1: 'a', key2: 'b'}
   # Instead of {"key1" => 'a', "key2" => 'b'} you can use:
   {"key1": 'a', "key2": 'b'}

 ,      ,     ,  :

 set = Set{41, 42, 43} # => Set{41, 42, 43}
 set.class # => Set(Int32)
   # The above is equivalent to
   set = Set(Int32).new
   set << 42
   set << 41
   set << 43
   set << 41
   set # => Set{42, 41, 43}


  4.
:      "="()?

        .        " "  :
 arr = input.split(" - ")
   curr = arr[0]
   rate = arr[1]
   
  :
   curr, rate = input.split(" - ")

   ,  , ,            ,           .   ,      (T,  K,  V)   , , Array (T), Hash (K, V)   ,  T   Int32, String, Char    .  :   ( ) .

  6  ,     ,      .




		_Nil-_ 

     ,       (nil);   "nilable"?   ,     Crystal       .
   nilable   ,       ,    "?",   :

__types_and_control_flow/type_nil.cr__
   n : Int32 | Nil
   n = 42
   p typeof(n) # => Int32

   a : Int32?
   a = 42
   p typeof(a) # => Int32


   b : Int32? 
   b = nil 
   p typeof(b) # => Nil

   a.try { p a + 1 } # => 43
   b.try { p b + 1 } # => no error!


  nil-  "try"    :       ,     .    ,       ,      !
       :


  5.
1.  .
    mineral    ?   if        :

arr = ["anadite", "humite", "roselite"]
mineral = arr[4]?

2.    ,     .     ?     "-" ,    ,    ,   .

h1 = Hash(Int32 | Char, Int32).new
  h2 = {} of String | Int32 => Bool | String

3.           var1,    if,  ,    ?

if rand < 0.5
   var1 = 7
   end # branches that are not present return nil!
   #
   var1 = 42
   if rand < 0.5
   var1 = "Crystal"
   end
   #
   var1 = 42
   if rand < 0.7
   var1 = "Crystal"
   else
   var1 = true
   end
   # 
   if rand < 0.5
   var1 = 42
   else
   var1 = "Crystal"
   end
   var2 = var1 * 2
   var3 = var1 - 1 # <= What does this return ?




	_      _

   ,  ,   ,      .      , Crystal    ,     ,       .    ,          .

    ,      ,  , , ,  ,    if-,     "nil"  "false".  ,    "if var1 && var2",   var1,  var2     .    ,  Crystal    .

    if      ,   :
  var1 = if 1 > 2 3 else 4 end # => 4
   # stated more concisely
   var2 = 1 > 2 ? 3 : 4 #=> 4


       var1   ?

# random choice between number and string
   var1 = rand < 0.5 ? 42 : "Crystal" #=> 42 or "Crystal"

 :   "Int32 | String".  ,    abs   var1  :

  typeof(var1) # => (Int32 | String)
  var1.abs # => Error: undefined method 'abs' for String

   ,  var1  Int32,        ,  "as",    :

 ivar1 = var1.as(Int32)

      ,    "rand",   var1 ӣ    String,   "as"      :

  cast from String to Int32 failed (TypeCastError)


  ,   "as?",       ,         "if ":

ivar1 = var1.as?(Int32) # => 42 or nil
   # or retaining only the value 42 with if:
   if ivar1 = var1.as?(Int32)
   p ivar1.abs # => 42
   end

   ,  var1  ,     :

 if var1.nil?
   # here var1 is nil
   end 

      .   "if var1"    "else"   ,     .

        "is_a?".   "if"       :

 var1 = 42
   if var1.is_a?(Number)
   # here var1 is a Number, which can be integer or floating point
   end

     ,   "case":

case var1
   when Number
   p var1 + 42
   when String
   p "we have a string"
   else
   p "var1 is not a number or a string"
   end
   # => 84

case     :      ,  ,   .       ,       ,     :

num = 42 
   case num
   when .even?
   puts "you have an even number"
   when .odd?
   puts "you have an odd number"
   end
   # => you have an even number

     "case"     . FizzBuzz     ,      "FizzBuzz",     3  5, "Fizz"     3,  "Buzz"     5,   1 
100.  "case"     :

(1..100).each do |i|
   case {i % 3, i % 5}
   when {0, 0}
   puts "FizzBuzz"
   when {0, _}
   puts "Fizz"
   when {_, 0}
   puts "Buzz"
   else
   puts i
   end
   end


%    ,      .  ,      ( )  '_'   ,   ,    .

      ,         .     "responds_to?":

var1 = "Crystal"
   if var1.responds_to?(:abs) # false in this case
   var1.abs
   end



  6.

1)    var1   "if"?

   if var1 = 1
   puts "inside if"
   var1 = 2
   puts var1
   end

          !


2)     ?

   if (num = 9) < 0
   puts "#{num}, is negative"
   elsif num < 10
   puts "#{num}, has 1 digit"
   else
   puts "#{num}, has multiple digits"
   end


3)        .     ?

begin
   a = 4 + 6
   rescue
   puts "an ex occurred"
   ensure
   puts a + 42
   end

:      .





		__
          ,   ,
   "case".  -, ,     ,        Crystal   ,     ,      . 

 ( )       .   ,        (  ,   Crystal  )    .






						 4.
						     .

        ,   ,         ,             .
 (  )     ,     ,     .         .      Ruby,       Crystal          .
   ,   " ",     .

  Crystal   ,    "   ",    ,   Int32  Char.   /  <=   . ,     
 ,     ,     (.  5, "   ").

 ,   ,        ,          ,     ,    ,       .  Crystal    ,     ,  Java  C#.      (        )   ""  .
         (.      )     .


 Crystal   .     Ruby  Python, Crystal   
 . ,    ,     .     Crystal: ,     ,        .

__methods_and_procs/methods.cr__
   x = 1 

   def add(y)
   x + y # Error: undefined local variable or method 'x'
   end

   add(2)



	_ _

    ,   ,    ,  
 ,     , -  .          ,         .      ,      ,   add   :

__methods_and_procs/methods.cr__
   def add(x, y)
   # return x + y # return is optional
   x + y
   end

1)  add(2, 3) # => 5
2)  add 2, 3 # => 5
3)  add(1.0, 3.14) # => 4.14
4)  add("Hello ", "Crystal") # => "Hello Crystal"
5)  add(42, " times") # => Error: no overload matches 'Int32#+' with type String
6)  add # => Error: wrong number of arguments for 'add'
   # (given 0, expected 2)

(      "   ".)


      (),      2.     , 
     ,          .      ,   .

  1, 3  4  ,        ,     .              .    add    "x"  "y"  ,   "Type"   ,         .

   ,    ,  Crystal       ,       .             .       (   5).

 Crystal     ,    ,    .    (   6)   ,    : [    "add" ( 0 ,  2)].

  "   "   overloading.cr,     add,  ,  Crystal   . (,      ,    .)


____________________________________________
  .

     "y" ,    Int,   "x":
def add(x, y : Int)
   x + y
   end
   
   add 3, 4 # 7
   add 2.14, 7 # 9.14

    add  "x"    Int.  "x"  "y"   
 Int,   : add (x: Int, y: Int).
___________________________________________


Crystal        .         (default)        ,       ,     show:

methods_and_procs/methods.cr__
   def show(x, y = 1, z = 2, w = 3)
   "x: #{x}, y: #{y}, z: #{z}, w: #{w}"
   end
   
   show 10 # => "x: 10, y: 1, z: 2, w: 3"
   show 10, 10 # => "x: 10, y: 10, z: 2, w: 3"
   show 10, 30, 2, 3 # => "x: 10, y: 30, z: 2, w: 3"
   show 10, 20 # => "x: 10, y: 20, z: 2, w: 3"
  
   show 10, z: 10 # => "x: 10, y: 1, z: 10, w: 3"
   show 10, w: 30, y: 2, z: 3 # => "x: 10, y: 2, z: 3, w: 30"
   show y: 10, x: 20 # => "x: 20, y: 10, z: 2, w: 3"
   show y: 10 # Error, missing argument: x


       "show"   ,         .    ,       .

    ,     ,    , 
   ,      "host", "client_id",  "client_secret":

require "oauth2"

   client = OAuth2::Client.new(
   host: "martian1",
   client_id: "7594",
   client_secret: "W*GDFUY75HSVS#@!"
   )



	_ _
	
      ,           . ,         ,     ,    :

__methods_and_procs/methods.cr__
   def typed_method : Array(Int32)
   (42..47).to_a.select { |n| n % 4 == 0 }
   end

   typed_method # => [44]

    Nil    ,   ,   "  " (   ,   ,   ,   ).
 ,   ,      ,   ,    
.

    ,       .  
    (    ң 4, "     " ):
__methods_and_procs/methods.cr__
   # Multiple return values
   def triple_and_array(s)
   {s * 3, s.split}
   end

 # unpacking:
   ret = triple_and_array("42") # => {"424242", ["42"]}
   ret[0] # => "424242"
   ret[1] # => ["42"]
   # or:
   num, arr = triple_and_array("gold")
   num # => "goldgoldgold"
   arr # => ["gold"]



  SPLAT *

  ,       ,      
   .        *,  
  -.           ,        .

 ,        :

__methods_and_procs/methods.cr__
   def salaries(*employees)
   employees.each do |emp|
   # calculate salary
   puts "#{emp}'s salary is: 2500"
   end
   end

   salaries() # =>
   salaries("Jones") # => Jones's salary is: 2500
   salaries("Baudelaire", "Rogers", "Gandhi")

   # => 
   # Baudelaire's salary is: 2500
   # Rogers's salary is: 2500
   # Gandhi's salary is: 2500


 -  ,        . Crystal   ,    *,  ,      "*"    .     :

def display(n, *, height, width)
   "The shape has height #{height} and width #{width}"
   end

display 3, height: 2, width: 5
   # => "The shape has height 2 and width 5"


       (  )    "" ,      :

__methods_and_procs/methods.cr__
   def increment(number, by value)
   number + value
   end

   p increment(10, by: 10) # => 20

        , 
 ,  ,         . ,   "1-2-3  " " 1-2-3- ".
       args,     "with joiner" (joiner   ,    ),  "with"   ,        :

__methods_and_procs/methods.cr__
   def join(*args, with joiner)
   String.build do |str|
   args.each_with_index do |arg, index|
   str << joiner if index > 0
   str << arg
   end
   end
   end

join 1, 2, 3, with: "-" # => "1-2-3"
join 1, 2, 3, 4, 5, with: "*" # => "1*2*3*4*5"



  1.
1. :   "total"      .
 "total" ,      .


2. -:
     (      " ң 4")    . ,     :
def add(n, m)
   n + m 
   end

tpl = {42, 108}
   add tpl 

 ?     ?

    :  *tpl,     .  tpl         ,      "**".   .

      -  "**argument"          .



	_  ,   _

      ,          .    ,  ,     ,              "yield" ().  ,  (Procs)      .

     -,      Ruby,       .     Ruby      "Using Procs"             .

        ,    ,    . (    ,      .)            ,     {}     "do"  "end".    ,          .

   Ruby,            . ,    "testing"       :
  testing do
   puts "in code block"
   end

        , "yield"      :
def testing
   puts "at top of method"
   yield
   puts "back inside method"
   yield
   puts "at end of method"
   end
   # => 
   # at top of method
   # in code block
   # back inside method
   # in code block
   # at end of method


       "yield",  Crystal         .            .         ,       .    , ,           .

  "yield"    ,          ,     do  "do |n|",  :

__methods_and_procs/procs.cr__
   def testing
   puts "at top of method"
   yield 1
   puts "back inside method"

 yield 2
   puts "at end of method"
   end

   testing do |n|
   puts "in code block #{n}"
   end

   # =>
   # at top of method
   # in code block 1
   # back inside method
   # in code block 2
   # at end of method


   break  ,     ;   next     ,    .

     ? ,  .    "" , 
 -  "def testing(&block)".       "Proc"   (     "testing do ..."),   .              .    "yield"    "Proc".       "block.call".

__methods_and_procs/procs.cr__
   def testing(&block)
   puts "at top of method"
   block.call
   puts "back inside method"
   block.call
   puts "at end of method"
   end

   testing do
   puts "in code block"

  end
   # =>
   # at top of method
   # in code block
   # back inside method
   # in code block
   # at end of method

 ,      ,  ,        {}.                   .

__methods_and_procs/procs.cr__
   langs = %w[Java Go Crystal]
   langs.map { |lang| lang.upcase } # => ["JAVA", "GO", "CRYSTAL"]

        ,   .    Crystal        (   Ruby).     map      langs,    .         .   ,        "&".

__methods_and_procs/procs.cr__
   langs = %w[Java Go Crystal]
   langs.map { |lang| lang.upcase } # => ["JAVA", "GO", "CRYSTAL"]
   langs.map &.upcase # => ["JAVA", "GO", "CRYSTAL"]

       ,      ,   :

__methods_and_procs/procs.cr__
   nums = [42, 43, 44]
   nums.map { |num| num + 2 } # (1) => [44, 45, 46]
   nums.map &.+(2) # (2) => [44, 45, 46]

        (.  " ").




	_      ( 3)_

 ,        ,   :

__methods_and_procs/curr_conv5.cr__
   base = "USD"
   rates = {
   "EUR" => 0.84320536,
   "CAD" => 1.26761115,
   "CNY" => 6.61591576,
   }
   full_names = { 
   "EUR" => "Euro", 
   "CAD" => "Canadian Dollar",
   "CNY" => "Chinese Yuan",
   }

   # How much is 1 US Dollar?
   output = "1 #{base} = \n" + # (1)
   rates.keys.map do |curr| # (2)
   "\t#{rates[curr]} #{full_names[curr]}s (#{curr})"
   end.join("\n") # (3)
   puts output
   # 1 USD =
   # 0.84320536 Euros (EUR)
   # 1.26761115 Canadian Dollars (CAD)
   # 6.61591576 Chinese Yuans (CNY)


   puts
   # How much is 42 US Dollars?
   amount = 42
   output = "#{amount} #{base} = \n" +
   rates.keys.map do |curr|
   temp = sprintf("%3.2f", amount * rates[curr]) # (4)
   "\t#{temp} #{full_names[curr]}s (#{curr})"


end.join("\n")
   puts output
   # 42 USD =
   # 35.41 Euros (EUR)
   # 53.24 Canadian Dollars (CAD)
   # 277.87 Chinese Yuans (CNY)


(1)   ,   "output=".
(2)         .        ,   "rates.keys".    "map"   ,    .  map          (rates)  .

(3)              .
(4)        ,  sprintf    "%3.2f".



  2.
 :
    (  )   1  10. (:  **.)
   langs       (:   sort_by).
          "langs".   : ["vaaJ", "oG", "ytsrlaC"]. (:   .)



 PROCS.

    ,     (    &block)   ,   ("Proc"),  -,   ;     "Proc"       "call".        :

1)   "->"    .      add     :

__methods_and_procs/procs.cr__
   fn = ->(n : Int32, m : Int32) { n + m }
   typeof(fn) # => Proc(Int32, Int32, Int32)
   fn.call(42, 108) # => 150

      "=".   "->"    . ,      .        {}.         ,  .

2)        "add",   :

__methods_and_procs/procs.cr__
   def add(n, m)
   n + m
   end

   fn = ->add(Int32, Int32)
   fn.call(42, 108) # => 150

3)  ,  Proc      ,      new:

__methods_and_procs/procs.cr__
   fn = Proc(Int32, Int32, Int32).new { |n, m| n + m }
   fn.call(42, 108) # => 150

     ,     "",  ,        ,       .         :

__methods_and_procs/procs.cr__
   n = 42
   fn = ->(m : Int32) { n + m }
   fn.call(108) # => 150
   n = 20
   fn.call(108) # => 128

    "n"   ,      ,   "n"   .       :

__methods_and_procs/procs.cr__
   def capture(&block : Int32 -> Int32)
   block
   end
   
 n = 42
 proc = capture { |m| n + m }
 proc.call(108) # => 150
   n = 20
   proc.call(108) # => 128

  ,      ,   :   "Int32 -> Int32" ,   "Int32"          "Int32".     ,  : "Int32, Int32 -> Int32".


  3.

ReturnProc:    (incr),     ""       "Proc".        .  ,              ?

    ?           .      ,            (.  "  ").
      !



	_   _
	
Crystal      -    .   .  "    " ,        .

,    ,       ,           .   (  ,        )      ,        ,     .

        .         ,     ,        .     ,    ,     -      ,       .

    ,   .    "add"      "overloading1234.cr" (. "    ").        add,  :
    ,     ,     "true";       "".
  ,     ,     (..    -  ).

 ,       ?  ,      "  ",     .

    .    [add (x:String, y:String)]  [ ,   ],  [add("Hello", "Crystal")],           "add".


   ,          .   "to_i" Σ   ,      (exception).         :

def add(x : String, y : String)
   if x.to_i? && y.to_i?
   add x, y # calls version 1
   end
   end

   add("Hello", "Crystal")    (nil),       (else)    :

def add(x : String, y : String)
   if x.to_i? && y.to_i? 
   add x, y # calls version 1
   else
   x + y
   end
   end


      :

__methods_and_procs/overloading.cr__
   # version 1:
   def add(x : Int, y : Int)
   x + y
   end

   # version 2:
   def add(x : Number, y : Number)
   x + y
   end

   # version 3:
  def add(x : Number, y : String)
   x.to_s + y # convert a number to a string with to_s method
   end

    # version 4:
   def add(x, y) 
   x + y
   end

   # new methods:
   # version 5:
   def add(x : Number, y : Bool)
   y ? x : 0
   end 

   # version 6:
   def add(x : String, y : String)
   if x.to_i? && y.to_i?
   add x.to_i, y.to_i # calls version 1
   else
   x + y
   end
   end

   add(2, 3) # => 5
   add(1.0, 3.14) # => 4.14
   add("Hello ", "Crystal") # => "Hello Crystal"
   add(42, " times") # => "42 times"
   add 5, true # => 5
   add 13, false # => 0
   add("12", "13") # => 25


      ,    (Unions)   .      add(x, y),     (nil),  [y  0],       [x+y].

 ,  "n = add(2, 3)".    ?  ,     n + 10?
  if,   .   c   ,   ,     .

       ,      .    ,  - (    " ").      ,          .     :

__methods_and_procs/overloading3.cr__
   def display(x : Number) # overloading 1
   puts "#{x} is a number"
   end

   def display(x : String) # overloading 2
   puts "#{x} is a string"
   end

   n = 42
   display n # => 42 is a number

   str = "magic"
   display str # => magic is a string

   r = rand < 0.5 ? n : str
   typeof(r) # => (Int32 | String)
   display r

   "display"    (1).    (# overloading 2),      .      :
     "r"  (Int32 | String).     "r"         (- "rand"),     ,    display  !

       "splat-",   :

__methods_and_procs/overloading3.cr__
   def method1(*args : Int32)
  end

   def method1(*args : String)
  end

   method1 41, 42, 43 # OK, invokes first overload
   method1 "Crystal", "Ruby", "Go" # OK, invokes second overload
   method1 1, 2, "No"
   # Error: no overload matches 'method1' with types Int32, Int32, String
   method1() # Error: no overload matches 'method1'

 ,     method1   .




	_     _

,      ,           "begin/rescue"   "  "?        . -,    ,   ,     :

__methods_and_procs/exceptions.cr__
   puts "Enter the numbers one by one, and end with an empty line:"
   input_array # => for example: [78, 56, 12]

   def input_array
   arr = [] of Int8
   while number = gets
   number = number.strip # removes leading or trailing whitespace
   if number == "" || number == "stop"
   break
   end
   add_to_array(arr, number)
   end
   arr
  end

   def add_to_array(arr, number)
   begin
   arr << number.to_i8
   rescue
   puts "integer bigger than 255"
   end
   end

  "add_to_array"     ("exception).      ,    "begin",   :

__methods_and_procs/exceptions2.cr__
   def add_to_array(arr, number)
   arr << number.to_i8
   rescue
   puts "integer bigger than 255"
   end

      "ensure" (.  ),        .




	_  _

      ,        "fact":

__methods_and_procs/factorial.cr__
   def fact(n)
   n == 0 ? 1 : n * fact(n - 1)
   end

  fact(5) # => 120 

      ,  ,  ,        :

   fact(-2) # => Runtime error: Invalid memory access
   (signal 11) at address 0x7ffbff7fdff8
   fact("Crystal") # => Error: undefined method '-' for String


      ,       :

__methods_and_procs/factorial2.cr__
   def fact(n : Int) : Int
   if n < 0
   raise ("n cannot be negative!")
   end
   n == 0 ? 1 : n * fact(n - 1)
   end

 fact(5) # => 120


   begin
   fact(-2) # => Runtime error: n cannot be negative! (Exception)
   rescue ex
   p ex.message
   end
   # => "n cannot be negative!"

 fact("Crystal") # => Error: no overload matches 'fact' with type String

     :   ,      .


                .         "begin-rescue",        (exit)  :

__methods_and_procs/factorial3.cr__
   def fact(n : Int) : Int

 if n < 0
   puts "n must be positive!"
   exit
   end
   n == 0 ? 1 : n * fact(n - 1)
   end


   fact(5) # => 120
   fact(-2) # => "n must be positive!"


          ,  .
         .    ,
    . :

def some_method(n : Int)
   return nil unless n > 1
   # other code, here n is > 1
   end

    (nil)   (false) ,    ,    .        unless,     ,    ,       .

,   .       ,       .


_______________________________________________
   .

 ,   Ruby,   Elixir,       ,         .
 ,       " ",              ,   .

   Crystal       (    ),      LLVM   .     ,   ,   ""     .
_____________________________________________


  4.
 :
  "Bubblesort"    (   )   .           "dup".     "Bubblesort",      ,  "yield"      .



		__
 ,   ,  Crystal         .     ,       .             ,       "Procs"     .
        . ,      .





					 5.
					   .

   -  ,     .  ,   ,         .        Crystal ,        "  ".         ,        .        .

 -  ,  ,  :     ,        .       ,   ,    ,    .
       Crystal. ,    ,        ,      .

  ,  ӣ     .          ,          .

  ,          :    typeof  , ,    ,      .
               , Crystal       .




	_  Ruby  Crystal_

   Ruby,       ,      .     Ruby  Crystal     ,   ,   ,         . (     Ruby,      .)
    Ruby:

__classes_and_structs/mineral.rb__
   class Mineral
   attr_reader :name, :hardness, :crystal_struct

   def initialize(name, hardness, crystal_struct)
   @name = name
   @hardness = hardness
   @crystal_struct = crystal_struct

  end
  end


   def mineral_with_crystal_struct(crstruct, minerals)
   minerals.find { |m| m.crystal_struct == crstruct }
   end


   def longest_name(minerals)
   minerals.map { |m| m.name }.max_by { |name| name.size }
   end

       ,   :

__classes_and_structs/mineral.rb__
   minerals = [
   Mineral.new("gold", 1, 'cubic'),
   Mineral.new("topaz", 8, 'orthorombic'),
   Mineral.new("apatite", 5, 'hexagonal'),
   Mineral.new("wolframite", 4.5, 'monoclinic'),
   Mineral.new("calcite", 3, 'trigonal'),
   Mineral.new("diamond", 10, 'cubic'),
   ] 
  
   min = mineral_with_crystal_struct('hexagonal', minerals)
   puts "#{min.crystal_struct} - #{min.name} - #{min.hardness}"
   # => hexagonal - apatite - 5

   puts longest_name(minerals)
   # => wolframite

       
$ ruby mineral.rb  :

 apatite - hexagonal - 5
   wolframite

   ,  ,           ?

__classes_and_structs/mineral.rb__
   # Runtime error:
   min = mineral_with_crystal_struct('triclinic', minerals)
   puts "#{min.crystal_struct} - #{min.name} - #{min.hardness}"
   # 3.5_mineral.rb:39:in `<main>': undefined method 'crystal_struct'
   # for nil:NilClass (NoMethodError)

Ruby ""   ,    "find"  "nil",     .      (nil)      .
Crystal       .    Ruby   Crystal,
    .     ӣ  ,      !

 ,  Crystal   :    Ruby   "mineral.cr"   
 $ crystal mineral.cr.
 Crystal      Ruby,       :

<=   Syntax error in mineral.cr:20: unterminated char literal,
   use double quotes for strings

   Mineral.new("gold", 1, 'cubic'),
   ^

    ,      ,  Crystal  !   
              :

	     :

Error in mineral.cr:2: undefined method 'attr_reader'


   attr_reader :name, :hardness, :crystal_struct
   ^~~~~~~~~~~



Crystal    getter (  , .  "     ")  "attr_reader", setter  "attr_writer",  property  "attr_accessor".         ,     .     2 "  Ruby  Crystal" (eBook).

     .      .  attr_reader  "getter"  ݣ  ()     ,         .      .         :
<=  
   Error in mineral.cr:20:
   instantiating 'Mineral:Class#new(String, Int32, String)'
   Mineral.new("gold", 1, "cubic"),
   ^~~
   in mineral.cr:5:

.../      "@ name"  Mineral/


  ,        "@ name: Type",        (   ) .


     :

1. "" @ name = 1 "" (   ),      .
2. "" @ name = Type.new "",       (.new).
3. "" @ name = Type.method "",   ("method")     ,    .
4. '@ name = arg',  'arg'        ('Type'),     .
5. '@ name = arg',  'arg'      ,       1, 2  3.
6. "@ name =  ",        (Type).
7. "@ name = LibSome.func ",  "" LibSome"   ,      func.
8. 'LibSome.func (out @ name)',  'LibSome'  ,       "func".

    (    8 )      .


Can't infer the type of instance variable '@name' of Mineral
(      @name  "Mineral")
@name = name
^~~~~


 Crystal        @name,   ,  
 .   @name = name,   .    , 
 @name  : "getter name : String".          :
	getter name : String
	getter hardness : Int32
	getter crystal_struct : String


ӣ       :

<=   Error in mineral.cr:25: instantiating
   'Mineral:Class#new(String, Float64, String)'
   Mineral.new("wolframite", 4.5, "monoclinic"),
   ^~~
   in mineral.cr:8: instance variable '@hardness' of Mineral
   must be Int32, not Float64
   @hardness = hardness
   ^~~~~~~~~


,    4,5 , ,    .   
 "@hardness"  "getter hardness: Float64",    ,        Int32.         .
          "":

<=   Error in mineral.cr:31: undefined method 'crystal_struct'
   for Nil (compile-time type is (Mineral | Nil))
   puts "#{min.crystal_struct} - #{min.name} - #{min.hardness}"
   ^~~~
   Rerun with --error-trace to show a complete error trace


     (nil)     ,      Ruby. Crystal     ,     :

$ crystal mineral.cr --error-trace
	# //,   Nil-trace:
   Nil trace:
   mineral.cr:30
   min = mineral_with_crystal_struct("hexagonal", minerals)
   ^~~
   mineral.cr:30
   min = mineral_with_crystal_struct("hexagonal", minerals)
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   mineral.cr:13
   def mineral_with_crystal_struct(crstruct, minerals)
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   mineral.cr:14
   minerals.find { |m| m.crystal_struct == crstruct }
   ^~~~
   /opt/crystal/src/enumerable.cr:409
   def find(if_none = nil)
   /opt/crystal/src/enumerable.cr:410
   each do |elem|
   ^
   /opt/crystal/src/enumerable.cr:413
   if_none
   ^~~~~~~
   /opt/crystal/src/enumerable.cr:409
   def find(if_none = nil)
   ^


 Nil    ,        ,
  -.      find,   "enumerable.cr:
def find(if_none = nil)

  ,  find   "nil"        ,    .

_______________________________________
    Crystal.

,   Crystal     Crystal. ,
  ,  Crystal    "Enumerable,  ,  
https://github.com/crystal-lang/crystal/blob/master/src/enumerable.cr. ,  .
______________________________________


        ,   
 .         (,   ,    ).    Crystal.
    ,      "Mineral | Nil".


   (. " "):   min    Mineral,
       ,     , min   (nil) . Crystal ,    ,    "min",      .    : .

      ,     Ruby:
 if min
   puts "#{min.crystal_struct} - #{min.name} - #{min.hardness}"
   else
   puts "No mineral found with this crystal structure!"
   end

     ,          "  ".  min  nil,   false     if  . ,    ,  ,   "min"  Mineral,     nil.    ӣ ,   nil,   Mineral.

,        ,    Ruby:
apatite - hexagonal - 5
   wolframite

       (  CoffeyScript). :
 def initialize(name, hardness, crystal_struct)
   @name = name
   @hardness = hardness
   @crystal_struct = crystal_struct
   end

  :
 def initialize(@name, @hardness, @crystal_struct)
   end


        , @name  gold, "@hardness"   1   :
  Mineral.new("gold", 1.0, "cubic")

      "initialize"    "getter", :
  def initialize(@name : String, @hardness : Float64, @crystal_struct : String)

    ,  ?

    Crystal   :
__classes_and_structs/mineral.cr__
   class Mineral
   getter name : String
   getter hardness : Float64
   getter crystal_struct : String

   def initialize(@name, @hardness, @crystal_struct)
   end
  end

   def mineral_with_crystal_struct(crstruct, minerals)
   minerals.find { |m| m.crystal_struct == crstruct }
   end

   def longest_name(minerals)
   minerals.map { |m| m.name }.max_by { |name| name.size }
   end

   minerals = [
   Mineral.new("gold", 1.0, "cubic"),
   Mineral.new("topaz", 8.0, "orthorombic"),
   Mineral.new("apatite", 5.0, "hexagonal"),
   Mineral.new("wolframite", 4.5, "monoclinic"),
   Mineral.new("calcite", 3.0, "trigonal"),
   Mineral.new("diamond", 10.0, "cubic"),
   ]

   min = mineral_with_crystal_struct("hexagonal", minerals)
   if min
   puts "#{min.crystal_struct} - #{min.name} - #{min.hardness}"
   else
   puts "No mineral found with this crystal structure!"
   end
   # => hexagonal - apatite - 5
   min = mineral_with_crystal_struct("triclinic", minerals)
   if min
   puts "#{min.crystal_struct} - #{min.name} - #{min.hardness}"
   else
   puts "No mineral found with this crystal structure!"

   end

   # => "No mineral found with this crystal structure!"

   puts longest_name(minerals)
   # => wolframite


Crystal          ,  Ruby.    Ruby        ,    ,       .



	_ _

      "     "     "".    ,  ,  ,    :

  class Mineral
   getter name : String
   getter hardness : Float64
   getter crystal_struct : String


   def initialize(@name, @hardness, @crystal_struct) # constructor
   end
   end


     ,    : name, hardness  crystal_struct.      Crystal.       ,   initialize:

__classes_and_structs/classes.cr__
   class Mineral
   getter name, hardness, crystal_struct


   def initialize(@name : String,
   @hardness : Float64,
   @crystal_struct : String)
   end
   end

      :
def initialize(@name : String = "unknown", ...)
   end


     :hardness   ,    .         .         (initialize).        .   "new"   Mineral:

min1 = Mineral.new("gold", 1.0, "cubic")
   min1 # => #<Mineral:0x271cf00 @crystal_struct="cubic",
   # => @hardness=1.0, @name="gold">
   min1.object_id # => 41012992 == 0x271cf00
   typeof(min1) # => Mineral # compile-time type
   min1.class # => Mineral # run-time type
   Mineral.class # => Class # all classes have type Class


, new    ,      .      ,   "initialize",      .      "" (   ),      (object_id),    .

       ,      .  ,       ,     .    ,       "initialize",        "T",      Mineralg:

___classes_and_structs/classes.cr___
   class Mineralg(T)
   getter name


   def initialize(@name : T)
   end
   end


   min = Mineralg.new("gold")
   min2 = Mineralg.new(42)
   min3 = Mineralg(String).new(42)

# => Error: no overload matches 'Mineralg(String).new' with type Int32

      "@".      "@@",  @@planet,      .  ,     ,       ,         . ( ,      ,        ,      .)

   ,   , , "quantity" ()   ,     "property".  ,         ,   "setter", , id    .
       :

__classes_and_structs/classes.cr__
   class Mineral
   @@planet = "Earth"

    getter name, hardness, crystal_struct
   setter id
   property quantity : Float32

   def initialize(@id : Int32, @name : String, @hardness : Float64,
   @crystal_struct : String)
   @quantity = 0f32
   end

   def self.planet
   @@planet
   end
   end

   min1 = Mineral.new(101, "gold", 1.0, "cubic")
   min1.quantity = 453.0f32 # => 453.0
   min1.id # => Error: undefined method 'id' for Mineral
   Mineral.planet # => "Earth"

  min2 = min1.dup
   min1 == min2 # => false


 ,           (initialize),     new.  ,    ,   "self.",         "planet".

  "dup"   ""  :  min2   ,     ,    ,       .     "" ,     "clone.       "finalize"  ,    ,    :

def finalize
   puts "Bye bye from this #{self}!"
   end

        .    ,         ,    Crystal   .
   ,     ,   :      ,   .    ,   ,    .

loop do
   Mineral.new(101, "gold", 1.0, "cubic")
   end

   Ruby   C#,     ,       :       .      .            ,   "String"  "Array"? (,      " ",      .)


  1.

1. :   Employee   (  "getter") "name"   "age".    Employee     .

2. :   Increment   "amount"     increment: ,   1  amount,  ,    "inc_amount".



	_ _

    - ,     Ruby,  Crystal   , :  < .    ,    ,          .  ,            ,  .

     ,  PDFDocument  "initialize", "name"  "print"   Document:
__classes_and_structs/inheritance.cr__
   class Document
   property name

   def initialize(@name : String)
   end

   def print
   puts "Hi, I'm printing #{@name}"
   end
   end

   class PDFDocument < Document
   end

   doc = PDFDocument.new("Salary Report Q4 2018")
   doc.print # => Hi, I'm printing Salary Report Q4 2018

        .   
  initialize  ,    .         ,        "super":

__classes_and_structs/inheritance.cr__
   class PDFDocument < Document
   def initialize(@name : String, @company : String)
   end

  def print
   super
   puts "From company #{@company}"

  end
  end


   # doc = PDFDocument.new("Salary Report Q4 2018")
   # => Error: wrong number of arguments for 'PDFDocument.new' (given 1,
   # => expected 2)
   doc = PDFDocument.new("Salary Report Q4 2018", "ACME")
   doc.print


   # => Hi, I'm printing Salary Report Q4 2018
   # From company ACME


   Crystal     .             ,  "print"  PDFDocument:

__classes_and_structs/inheritance.cr__
   class PDFDocument < Document
   def initialize(@name : String, @company : String)
   end


   def print(date : Time)
   puts "Printing #{@name}"
   puts "From company #{@company} at date #{date}"
   end
   end


   doc = PDFDocument.new("Salary Report Q4 2018", "ACME")
   doc.print(Time.now)


   # => Printing Salary Report Q4 2018
   # From company ACME at date 2017-05-25 12:12:45 +0200




     .

 Ruby,    Java  C#,        .   Ruby,   Crystal     ,      .        ,     -,        .

     ,      .        ,    .     Rect ( ),        Shape:

__classes_and_structs/inheritance.cr__
   abstract class Shape
   abstract def area
   abstract def perim
   end

   class Rect < Shape
   def initialize(@width : Int32, @height : Int32)
   end

   def area
   @width * @height
   end

   def perim
   2 * (@width + @height)
   end
   end

   s = Shape.new # => can't instantiate abstract class Shape
   Rect.new(3, 6).area # => 18


    (, perim)  ,   ,   :
error: "abstract `def Shape#perim()` must be implemented by Rect"

     ,     ,       .         .     Document    ,                 :

__classes_and_structs/virtual.cr__
   class Document
   end

   class PDFDocument < Document
   def print
   puts "PDF header"
   end
   end

   class XMLDocument < Document
   def print
   puts "XML header"
   end
   end
   class Report
   getter doc

   def initialize(@name : String, @doc : Document)
   end
   end

   salq4 = Report.new "Salary Report Q4", PDFDocument.new
   taxQ1 = Report.new "Tax Report Q1", XMLDocument.new


       Document+,   ,       Document,      Document.        ,   ,   ,  "d"      (PDFDocument | XMLDocument):

 if 4 < 5
   d = PDFDocument.new
   else
   d = XMLDocument.new
   end
   typeof(d) # => Document


,  , d   Document.        Document+    (PDFDocument | XMLDocument),          .

    ,   Document,  :
 salq4.doc.print # => Error: undefined method 'print' for Document

   ,    Document .
__classes_and_structs/virtual.cr__
   abstract class Document
   end

   salq4.doc.print # => PDF header


  2.

:  Shape   Square  Circle. (:  PI   Math,  : "include Math".)




	__ __

          ,        .   (   )     - .             ()   ,    .    : ӣ        .

         ,    :
       ,          .         ,  .

 Crystal     .           ,    .   ,        :  "private" (    ),  "protected" (  ).



  .

     :           ,      .

__classes_and_structs/private.cr__
   class Document
   property name

   def initialize(@name : String)
   end

   private def print(message)
   puts message
   end

   def printing
   print "Hi, I'm printing #{@name}"
   # self.print "Printing with self does not work"
   # => Error: private method 'print' called for Document
   end
   end

   class PDFDocument < Document
   def printing
   super
   print "End printing PDFDocument"
   end
   end


   doc = Document.new("Salary Report Q4 2018")

doc.printing # => Hi, I'm printing Salary Report Q4 2018


   pdoc = PDFDocument.new("Financial Report Q4 2018")
   pdoc.printing # =>
   # Hi, I'm printing Financial Report Q4 2018
   # End printing PDFDocument
   # doc.print("test") # => Error: private method 'print' called for Document


 ,   (enums),    .          ,    .

             ,          .      "private"  ,   ,      ,    .



 .

   c :      ,     ,  ݣ     .       ,    ( )  , , ,     ,       .   ,     :

__classes_and_structs/protected.cr__ 
   class Document
   property name

   def initialize(@name : String)
   end

   protected def print(message)
   puts message
   end
 
   def printing
   print "Hi, I'm printing #{@name}"
   self.print("This works too: self is a Document")
   doc = Document.new("Taxes")
   doc.print("This also: doc is a Document")

 end
  end


   class BankAccount < Document
   def printing
   doc = Document.new ("TestDoc")
   doc.print "inside BankAccount"
   end
   end


   class BankAccount2
   def printing
   doc = Document.new ("TestDoc")
   doc.print "inside BankAccount2"
   end
   end


   doc2 = Document.new "Audit 2017"
   doc2.printing
   # => Hi, I'm printing Audit 2017
   # => This works too: self is a Document
   # => This also: doc is a Document
   doc2.print "Audit" # => Error: protected method 'print' called for Document
   ba = BankAccount.new "test"
   ba.printing # => inside BankAccount
   ba2 = BankAccount2.new
   # ba2.printing # => Error: protected method 'print' called for Document


  "print"    BankAccount,    ,   Document,         .        BankAccount2,       Document,     :
 "error: protected method 'print' called for Document"



 .

,      ,    .        "==". ,      ,     ID (.. ).

     Minerals,     "self"    .        "compare":

__classes_and_structs/classes.cr__
   class Mineral
   getter id, name, hardness, crystal_struct
   property quantity : Float32


   def initialize(@id : Int32, @name : String, @hardness : Float64,
   @crystal_struct : String)
   @quantity = 0f32
   end


   def ==(other : self) # self is Mineral
   id == other.id
   end


   def ==(other)
   false
   end


   def self.compare(m1 : self, m2 : self)
   m1.id == m2.id
   end
   end


   m1 = Mineral.new(101, "gold", 1.0, "cubic")
   m2 = Mineral.new(108, "gold", 1.0, "cubic")
   m3 = Mineral.new(101, "gold", 1.0, "cubic")
   m1 == m2 # => false

  m1 == m3 # => true
  Mineral.compare(m1, m2) # => false



 ,    Ruby  "class << self",    ,    Crystal.         "self".

  :       ? ( Ruby   "".)
 ,    Crystal     .  ,   :

__classes_and_structs/classes.cr__
   class Mineral
   getter id, name, hardness, crystal_struct
   property quantity : Float32

   def initialize(@id : Int32, @name : String, @hardness : Float64,
   @crystal_struct : String)
   @quantity = 0f32
   end

   def initialize(@id : Int32)
   @quantity = 0f32
   @name = "rock"
   @hardness = 0
   @crystal_struct = "unknown"
   end
   end

   m1 = Mineral.new(101, "gold", 1.0, "cubic")
   m4 = Mineral.new(42)
   # => #<Mineral:0x271bd40
   # @crystal_struct="unknown",
   # @hardness=0,
   # @id=42,


  # @name="rock",
  # @quantity=0>


___________________________________________________________________________________
.

 Ruby       (  main)  Object:
 # Ruby code!
   puts self # main
   puts self.class # Object

       Crystal,    : "  self (..     )".    self      .     self   " ",     Ruby:

__classes_and_structs/program.cr__
   def display
   puts "Top-level display"
   end

class Mineral
   puts self # => Mineral
   getter name
   getter hardness
   getter crystal_struct

   def initialize(@name : String, @hardness : Float64,
   @crystal_struct : String)
   end

   def display
   ::display # => Top-level display
   p self # => <Mineral:0x271cf00 @crystal_struct="cubic",
   # @hardness=1.0, @name="gold">
   end
   end

  min1 = Mineral.new("gold", 1.0, "cubic")
  min1.display


     (  display)             (::).   Crystal   "main",         ,  "the Program",    ,  "puts, "p", "raise", "spawn"  ,      .

 Crystal    ,     ( ) , , ,    , ...    ݣ.
____________________________________________________________________________



	_  _

,   ,    " "        .            " ",        .

            ,   "struct"      "Struct".     ,    (  ),  ţ     .  :    .        :  ,   ,     .
       :

__classes_and_structs/structs.cr__
   struct User
   property name, age

   def initialize(@name : String, @age : Int32)
   end

   def print
   puts "#{age} - #{name}"
   end
   end

   d = User.new("Donald", 42)

 d.name # => Donald
   d.age = 78
   d.print # => 78 - Donald


_____________________________________________________
   .

    ,           .     no_change   ,    "change":
__classes_and_structs/structs.cr__
   def no_change(user)
   user.age = 50
   end

   def change(user)
   user.age = 50
   user
   end

   d = User.new("Donald", 78)
   d.print # => 78 - Donald
   no_change(d)
   d.print # => 78 - Donald
   d = change(d)
   d.print # => 50 - Donald
_________________________________________________


      (  )  ,          .       ,       Complex.
      ,     .


_____________________________________________________
   .

          ,   .         ,     .
___________________________________________________


  3.
 Vec2D -    : ,       .   Vec2D        "+". 
 ݣ   ,     . (:   "+",  "self"     Vec2D.)



	_  _

  , , ,     Crystal    .
        . :
$ crystal tool hierarchy virtual.cr

       ,     .        (,    ),   -e:
$ crystal tool hierarchy -e Document virtual.cr

    :

	\. (eBook)\



      ,    ,   (, Int32),      .        (.. ),     .

	\.  \


  , , Object,       ,      "=="  "to_s". ,            Value,     ,     Reference,     "  ".

     "- "      ,      ,   ,     .        ,    ,    ,  ""   .

  " "     ,    .  "Exception"      " ".        -    (),        :
__classes_and_structs/types.cr__
  alias PInt32 = Pointer(Int32)



	_  _
	
,       ,      Crystal,     ,    .



TO_S (IO).

    ,     ,    .
 Java   ,  toString(). -  to_s,    "Object".  "to_s"     Crystal.      ,      "to_s(IO)".     ,  "String"   "- ":       ,     ,        " ".

 -,          .  
     "<<"    IO      ,  "to_s"  ,      to_s :
__classes_and_structs/useful.cr__
   class Mineral
   getter name, hardness

   def initialize(@name : String, @hardness : Float64)
   end

   # Good
   def to_s(io)
   io << name << ", " << hardness
   end

   end


   min1 = Mineral.new("gold", 42.0)
   io = IO::Memory.new
   # To see what io contains, use to_s:
   min1.to_s(io).to_s # => "gold, 42.0"


IO         Crystal,     :   , ,   ..




    .

,  "Exception"  ,    ,     ,  
"IndexError", "TypeCastError", "IO::Error"  .. ݣ      :
  class CoolException < Exception
   end

   raise CoolException.new("Somebody pushed the red button")
   # => Somebody pushed the red button (CoolException)


   !     ,       ,      ""   rescue-:

__classes_and_structs/useful.cr__
   ex = begin
   raise CoolException.new
   rescue ex1 : IndexError
   ex1.message
   rescue ex2 : CoolException | KeyError
   ex2.message
   rescue ex3 : Exception
   ex3.message
     rescue # catch any kind of exception
   "an unknown exception"
   end # => "ex2"


         ,            JSON,      :

__classes_and_structs/useful.cr__
   require "json"
   path = "path/to/file"

   begin


if File.exists?(path)
   raw_file = File.read(path)
   map = JSON.parse(raw_file)
   File.write(path, "ok")
   :ok
   end
   rescue JSON::ParseException # Parsing error
   raise "Could not parse file"
   rescue ex
   raise "Other error: #{ex.message}"
   end




  .

    ,    "  ,   ",         .     ,       .      "after_save"    ,    "save"  ,         :

__classes_and_structs/useful.cr__
   class MineralC
   def initialize
   @callbacks = [] of ->
   end


   def after_save(&block)
   @callbacks << block
   end


   # save in database, then execute callbacks
   def save
   # save


 rescue ex
   p "Exception occurred: #{ex.message}"
   else
   @callbacks.each &.call
   end
   end
   min = MineralC.new
   min.after_save { puts "Save in DB successful" }
   min.after_save { puts "Logging save" }
   min.after_save { puts "Replicate save to failover node" }
   min.save # =>
   # Save in DB successful
   # Logging save
   # Replicate save to failover node



  4.
1)   :
  ,       :
  x = rand < 0.0001 ? 1 : "hello"
  x - 1 # => Error: undefined method '-' for String
    "String"    '-'.    -       ,   .
2)   :
,     "p   ,   Crystal,     Ruby. 	    :
class A
   def b
   41
   end
   end

# this can also be written on 1 line as: class A; def b; 41; end; end;


   p A.new.b

   class A
   def b
   42
   end
   end

   p A.new.b


      ,       ԣ    "previous_def ".



		__
,         .     "private  "protected"       ,     .         .      ,      ݣ   .






							 6.
							  .
						
     ,       .       ,               ,     .    (   )   ,       Crystal.


	_    "Require"_

           ,      
 .  ,         , , ,       -.       ,  "require",     :

require "path"

 "path"      (  ),       .    "require",               ,      .           .     :   "require    .

    ,       .  ,      .  ,    , ,        .   ,      -:

	 fileA.cr
	 dirA
		 dirB.cr
		 fileA.cr
		 fileA2.cr
		 fileA3.cr
		 dirB
			 dirB.cr
			 fileB.cr
		 dirC
			 fileC1.cr
			 fileC2.cr


  .

  require "./part"   part.cr ,    , part/part.cr ( "part/"  )   .   :
__working_with_modules/require.cr__
   require "./fileA" 
   # I am from fileA.cr in the current folder 
   # OR (if present): fileA.cr in subfolder fileA 
   require "./dirA/fileA" 
   # I am from fileA.cr in dirA 
   require "./dirA/dirB" 
   # I am from dirB.cr in dirA 
   # OR: I am from dirB.cr in dirB in dirA

    ,     require "./part.cr",  
 .


  .

    ,    [require "../part"],      ,   ,          .
__working_with_modules/require.cr__
   require "../fileA" 
   # I am from fileA.cr in the parent folder 
   require "../dirA/fileA" 
   # I am from fileA.cr in dirA in the parent folder

       ,  :
 require "../../part".



    .

   ,   :
__working_with_modules/require.cr__
   require "./dirA/dirB/fileB" 
   # I am from fileB.cr, in dirB in dirA


     .

    (*)  require "./dirA/*"    ".cr"   dirA,     .
__working_with_modules/require.cr__
 require "./dirA/*" 
   # I am from fileA2.cr in dirA 
   # I am from fileA3.cr in dirA

          (*),    require "./dirA/**".      ".cr"   dirA,     ,   dirC.
__working_with_modules/require.cr__
   require "./dirA/**" 
   # I am from fileC1.cr in dirC in dirA 
   # I am from fileC2.cr in dirC in dirA



    Crystal.

   ,   Crystal,   [require "file"].
__working_with_modules/require.cr__
   require "file"

  "file.cr"   "file/file.cr"   ,    .       ,   Linux    /opt/crystal/src.      -,      ,      Crystal.
     lib    .       ,    ,   .

 c         .     ,     Crystal     .

_____________________________________________
 .

 -  ,     "require",    , , ,    ,    ?
 ,        "prelude",      Linux   /opt/crystal/src/prelude.cr   ,   Crystal.       prelude.cr   GitHub.
__________________________________________



	_     _

            ,     .      ,    ݣ      ,     .

        ( ).    ,      ,    , ,     .       "     ".   :

__working_with_modules/namespaces.cr__
   module Crystals
   class Rhombic
   end

   class Triclinic
   end
   end

   t = Crystals::Rhombic.new
   typeof(Crystals) # => Class


 ,        ("Crystals :: Rhombic.new").   ,      "Class".       ? ,    .

   ,     ""  (       ,     ).  ,       ,   :
        (-  Module.method),     Σ  . ,   ,      ,     .

  ,  , :
__working_with_modules/trig.cr__
   module Trig
   PI = 3.141592654

def self.sin(x)
   puts "Calculating the sin of #{x}"
   end

   def self.cos(x)
   # .. 
   end
   end


 trig.cr   Trig    ,  ,  sin.

 , ,     (  ,     ).    moral.cr     "Moral",    sin.

__working_with_modules/moral.cr__
   module Moral
   VERY_BAD = 0
   BAD = 1


   def self.sin(badness)
   puts "Assessing the sin of #{badness}"
   end
   end


            self        "Module.method" (   ,  ).

  "require"        :

__working_with_modules/namespaces.cr__
   require "./trig"
   require "./moral"

   y = sin(Trig::PI/4) # => Error: undefined method 'sin'
   y = Trig.sin(Trig::PI/4) # => Calculating the sin of 0.7853...
   wrongdoing = Moral.sin(Moral::VERY_BAD) # => Assessing the sin of 0

 ,     ,      (, Trig.sin  Moral.sin),      .


  1.
   Sin:
           ,    , ,          .    ,       Crystal,      "sin". (:   Math   ,    "require".   "math_sin.cr".)

 ,      ,    .  ,    "protected  :

__working_with_modules/visibility.cr__
   module Languages
   class Crystal
   protected def shout
   puts "Hello, I am written in Crystal"
   end
   end
   class Ruby
   def shout
   Crystal.new.shout
   end
   end
   end

   Languages::Ruby.new.shout # => Hello, I am written in Crystal
   Languages::Crystal.new.shout
   # => Error: protected method 'shout' called for Languages::Crystal

  ,   Crystal  Ruby        ,  "Languages".      ,   "shout"   Crystal   ,         .



	_   _

 self     -    .     ,   :
__working_with_modules/moral2.cr__
   module Moral
   extend self
   VERY_BAD = 0
   BAD = 1


   def sin(badness)
   puts "Assessing the sin of #{badness}"
   end
   end

 ,        ,  Math     .   "extend",    ,      "Module.method", (, Math.sin).         ,    ! :     Class.
__working_with_modules/namespaces.cr__
   require "./moral2"

    y = Math.sin(Math::PI/4) # => 0.70710678118654746
   wrongdoing = Moral.sin(Moral::VERY_BAD) # => Assessing the sin of 0


   "include"  ,     ,       ,     :
__working_with_modules/namespaces2.cr__
   require "./moral2"
   include Moral

   y = Math.sin(Math::PI/4) # => 0.70710678118654746
   wrongdoing = sin(Moral::VERY_BAD) # => Assessing the sin of 0



	_  _

     ,          .  ,     ""   ,    Ruby  Dart.       include;        ( ).

      "include ModuleName",     
   ,     !      "     ".    :
  (DVD  BlueRay)     Debug,  ,    ,     "who_am_i?" :

__working_with_modules/mixins.cr__
   class Basic
   def initialize(@name : String)
   end

   def to_s
   @name
   end
   end

   module Debug
   def who_am_i?
   "#{self.class.name} (\##{self.object_id}): #{self.to_s}"
   end
   end

   class DVD < Basic
   include Debug
   # ... 
   end

   class BlueRay < Basic
   include Debug 
   # ... 

 end 
  
 
 dv = DVD.new("West End Blues")
 br = BlueRay.new("Attack of the Martians")
 dv.who_am_i? # => DVD (#40886016): West End Blues
 br.who_am_i? # => BlueRay (#40885984): Attack of the Martians


   "\#"    ?    ,          #.
      ,      extend:
__working_with_modules/mixins.cr__
   module DebugC
   def who_am_i?
   "#{self.class.name}: #{self.to_s}"
   end
   end

   class CD < Basic
   extend DebugC
   # ...
   end

   cd = CD.new("Bach's Cello Suites")
   cd.who_am_i? # => Error: undefined method 'who_am_i?' for CD
   CD.who_am_i? # => "Class: CD"

     .   ,   "include"    ,         .       "  ".

   ( )  ,          ;  ,      .             ? ,     ,    .



	_   _
	
            ?    :
  ( )   ,      ,     ()     .      ,         ( ,  ).    ,      :
  error: undefined method 'method_name' for 'class_name

     Ruby,       NoMethodError.  ,     ,          ,        ?    " ",      . ,     :

__working_with_modules/ancestors.cr__
   module M1
   def meth1
   41
   end
   end

   class C1
   def meth1
   42
   end 
   end

   class D1 < C1
   include M1
   end

   class E1 < D1
   end

   E1.new.m1 # => Error: undefined method 'm1' for E1
   E1.new.meth1 # => 41 # meth1 from module M1 is called


  meth1   M1,   "meth1   C1.     ,  : E1   D1,    M1,    C1,   ,   .



	_  _

   Crystal,   ,           .      .

 .
,       ,   "<", ">=",  ݣ "==".        ?  ,     Comparable(T)     .   ,       . 

   " ": Comparable    ,      .    "  ": abstract def <=> (other : T).
      "<=>".     :       ,    "-1", "0  "1",    ,
   ", ""  ""   .  
  ,     Person   :

__working_with_modules/comparable.cr__
   class Person
   include Comparable(Person)

   getter name, age


   def initialize(@name : String, @age : Int32)
   end


   def <=>(other : self) # other must be of type self, which is Person
   if self.age < other.age # here self is the current Person object
   -1
   elsif self.age > other.age
   1
   else # == case
   0
   end
   end
   end

  "<=>"     ,       :

__working_with_modules/comparable.cr__
   def <=>(other : self) # other must be of type self, which is Person
   self.age <=> other.age
   end


       "comparable.cr" [ github.com/crystal-lang/crystal/blob/master/src/comparable.cr]       Crystal!  ""?    ,    "" (.. ):      "T",   "<=>".        : 
include Compatible (Person).


 .

          ,    .     ,      each,       ,   (yield).
     "Sequence",           "@top".  Sequence     Enumerable:

__working_with_modules/enumerable.cr__
   class Sequence
   include Enumerable(Int32)


   def initialize(@top : Int32)
   end


   def each
   0.upto(@top) do |num|
   yield num
   end
   end
   end


   seq = Sequence.new(7)
   # using some methods of module Enumerable:
   seq.to_a # => [0, 1, 2, 3, 4, 5, 6, 7]
   seq.select &.even? # => [0, 2, 4, 6]
   seq.map { |x| x ** 2 } # => [0, 1, 4, 9, 16, 25, 36, 49]


  .
  Enumerable     :    .       ,       ,    .
             ,    ,        .      .    Enumerable,     .   "Iterator",      "next".        "of" (  "next"),    :

__working_with_modules/iterator.cr__
   n = 0
   inc = Iterator.of do
   n += 1
   n
   end

   inc.next # => 1
   inc.next # => 2
   inc.next # => 3

   n = 0
   m = 1
   fib = Iterator.of { ret = n; n = m; m += ret; ret }

   fib
   .select { |x| x.even? }
   .first(10)
   .to_a # => [0, 2, 8, 34, 144, 610, 2584, 10946, 46368, 196418]

   ,     ( 10),   .


  2.
    comparable.cr,    ,     ͣ?



		__
     ,   ,        ,   .     ,    :  " "  "".

     .       ,      ,    .       ,      Crystal;          ,  ,    ,     ( ,    ).

      ,    Crystal      .







							 7.
							 .
						
    ,       Crystal.     Crystal  , ,      . ,       ,          ( ).      ,      ,     ;  ݣ      !


	_ Shard_
    Crystal  GitHub,   "sentry tool"   MongoDB.  ,       ,    .     :        Crystal.     "New Application"         , 
     .     Crystal,  (.  , ),            .


       :
  $ crystal init TYPE NAME [   [DIR]

TYPE    "app" ( )  "lib" (     ). NAME  , ,  .   DIR   ,       .    ,  TYPE      "shard.yml":       "targets".

     "proj1":
  $ crystal init app proj1
  ,     :
<=   $ crystal init app proj1
   create proj1/.gitignore
   create proj1/.editorconfig
   create proj1/LICENSE
   create proj1/README.md
   create proj1/.travis.yml
   create proj1/shard.yml 
   create proj1/src/proj1.cr
   create proj1/spec/spec_helper.cr 
   create proj1/spec/proj1_spec.cr 
   Initialized empty Git repository in ~/proj1/.git/


 ,     :
     (   proj1).
  README  LICENSE.
  .travis.yml,     Travis     .
  shard.yml    (    "gemfile"   ).
  "src",    .
   "spec",   .
     Git,     (  )      GitHub   .
  .editorconfig,        ,          .

    (     ),  "lib1"  "lib2",      :
  proj1
	  bin
	  lib
		  lib1
			  lib1.cr
		  lib2
			  lib2.cr
	  src
		  proj1.cr
		  spec
			  proj1_spec.cr
			  spec_helper.cr


     "lib".         bin.
  .  ,       .  "proj1.cr"        :
# TODO: Write documentation for `Proj1`
   module Proj1
   VERSION = "0.1.0"

    # TODO: Put your code here
   puts "app proj1 is started"
   end


  puts,        .       "proj1_spec.cr"     :
  require "./spec_helper"

     spec_helper.cr,    :
  require "spec"
  require "../src/proj1"

     , "spec".       "proj1.cr".   ,      , ?      ?       "proj1"   :
$ crystal src/proj1.cr
Crystal        .     App 'proj1' is started,    .

   ,   :
$ crystal build src/proj1.cr

   "proj1"    "proj1",     :
$ ./proj1


             .      (   , ) ,      .      -  ( !).         ,       "-- release"   .

    MongoDB  Crystal [github.com/datanoise/mongo.cr.].      ,       "require"  (..     ).


  1.
    ,  "mineral", (  "crystal init")         (2 "  Crystal, 5 "   ")  ,  .



	_ _

                     "".     "gofmt" (  Go)  Rustfmt (  Rust), Crystal           .

       ".cr"        ,    ,    Crystal.    :
$ crystal tool format file.cr

       ,  Crystal       .       ,     ( Sublime Text)    (  Visual Studio Code),  "tool format" ,      .     ,     -  ,    (, classes_and_structs/classes.cr),  ,      .



	_ _
	
    , ? Crystal     :        "Markdown".    -  ,    "#",          .

   [crystal-lang.org/docs/conventions/documenting_code.html].
         "mineral" (    ,   ?).  :
__managing_projects/mineral/src/mineral.cr__
   require "./mineral/*"

   module Mineral
   puts "app mineral is started!"
   module Hardness
   def data
   mohs = {"talc" => 1, "gold" => 2.5, "calcite" => 3,
   "apatite" => 5, "corundum" => 9}
   end


   def hardness
   data[self.name]
   end
   end


   # Every Mineral has **hardness** (see the `Hardness` module).
   #
   # To create a standard rocky Mineral:
   # 
   # ```
   # min1 = Mineral.new(108)
   # min1.to_s
   # ``` 
   #
   # The above produces:
   #
   # ```text
   # "This is a mineral with id 108 and is called rock"
   # ``` 
   #
   # Checks the hardness with `#hardness`.
   class Mineral
   include Hardness
   getter id, name
   setter crystal_struct


   # Creates a mineral with given parameters
   def initialize(@id : Int32, @name : String, @crystal_struct : String)
   end


   # Creates a mineral with name "rock", 0 hardness and "unknown" structure
   def initialize(@id : Int32)
   @name = "rock"
   @crystal_struct = "unknown"
   end


# Prints out a description of this mineral
   def to_s
   puts "This is a mineral with id #{id} and it is called #{name} "
   puts "It has #{crystal_struct} as crystal structure"
   end


   # Returns object properties in csv-format
   def to_csv
   "#{id},#{name},#{hardness},#{crystal_struct}"
   end
   def ==(other : self)
   id == other.id
   end


   def ==(other)
   false
   end


   # Returns crystal structure of this mineral
   def kind_of_crystal
   @crystal_struct
   end


   # :nodoc:
   class Helper # no docs are created for this class
   end # neither for private or protected classes
   end
   end


Crystal     ,      API  .      ,        .   :
$ crystal docs

   doc,        -.  ţ ,   "doc/index.html".     -,   "Mineral".

	\.\


 -  BUG, DEPRECATED, FIXME, NOTE, OPTIMIZE  TODO   .   ,       ,   ݣ  .



	_    Spec_
	
,         ,  ,      . :        ,   .  ,     ,       ,        . ݣ          .

Crystal       ( "spec"),    "RSpec",   Ruby.   "spec"     ,   -,         "_spec".

    "mineral/spec/mineral_spec.cr"   (  require):
   describe Mineral do
   # TODO: Write tests


   it "works" do
   false.should eq(true)
   end
   end


   Ruby, , ,    "describe, it"  "should",      DSL:
 "describe"    .
 "it" ,   .  ţ    ,      "do  end".
 "should"   ,        .


      ,      "  ,   ".
       "works", , ,       .       "spec",       :
$ crystal spec

  :

		\.\



  ,    (should),  ,    (got),      :  Mineral works  .
 ,      .       F,   .
       (.).       ( ,     )       "crystal spec".      ,    (nm)   ,   :
$ crystal spec spec/file_spec.cr:nm

    ,  ,    ,    ,     .       ,      .
 ,    "spec"   "--release",         ,     .   "--release"    .        (  "works")      ,    ̣    0 failures ( ).


   ,     ,    "spec",          .        .

    :
  ,        ;
      ;
  CSV    "to_csv";
   "hardness"   Hardness;
   "=="  "true"     ;
   "=="  "false"    ;
    "kind_of_crystal"  ;


   ,     :

__managing_projects/mineral/spec/mineral_spec.cr__
   require "./spec_helper"


   describe Mineral do
   # TODO: Write tests


   # it "works" do 
   # false.should eq(true)
   # end 


   it "creates a default mineral" do
   min1 = Mineral::Mineral.new(108)
   min1.id.should eq(108)
   min1.name.should eq("rock")
   min1.crystal_struct.should eq("unknown")
   end 


   it "creates a mineral with parameters" do
   min1 = Mineral::Mineral.new(42, "apatite", "hexagonal")
   min1.id.should eq(42)
   min1.name.should eq("apatite")
   min1.crystal_struct.should eq("hexagonal") 
   end


   it "creates correct csv format" do
   min1 = Mineral::Mineral.new(101, "gold", "cubic")
   min1.to_csv.should eq("101,gold,2.5,cubic")
   end


   it "gold has hardness 2.5" do
   min1 = Mineral::Mineral.new(42, "gold", "cubic")
   min1.hardness.should eq(2.5)
   end


   it "== works for same mineral" do
   min1 = Mineral::Mineral.new(42, "gold", "cubic")
   (min1 == min1).should eq(true)
   end 
   it "== works for different mineral" do
   min1 = Mineral::Mineral.new(42, "gold", "cubic")
   min2 = Mineral::Mineral.new(43, "corundum", "trigonal")
   (min1 == min2).should eq(false)
   end


   it "kind_of_crystal works" do
   min1 = Mineral::Mineral.new(42, "gold", "cubic")
   (min1.kind_of_crystal).should eq("cubic")
   end
   end


        "spec",       .



	_  _

   ,    " ".    ,             Crystal.   "shards",     (gems)   .

 "shards"   ݣ   ,    ,       Ruby.           -?
        shard.yml ( gem-  ),     .         :

  name: mineral
   version: 0.1.0

   authors:
    - Your-Name <your-email-address>


  targets:
    mineral:
    main: src/mineral.cr


  crystal: 0.22.0


  license: MIT


      ,  ݣ  ,     .  ""  ,       .



 SHARD.

 ң   ,   -   .    "katip" ( - Guven Cenan)   -.        ,     "mineral_log",         "mineral".
  ,   -   Crystal.
    shard.yml    "mineral_log",   ,    :
  dependencies:
   katip:
   github: guvencenanguvenal/katip


             GitHub;       ,        Crystal.

        :
version:  0.1.2

Crystal,  ,      ,        ,    .       (  ),   "shard.yml" ݣ  :
branch: master


            .          :
$ crystal shards
 ݣ :
$ shards
    :
Updating https://github.com/guvencenanguvenal/katip.git
Installing katip (version: 0.1.0)

       -,    .   ,      :
$ shards list
     :
Shards installed:
  * katip (0.1.0)


   ,   ,    :
  lib,       .  , 
 ݣ  ,    .
   ".shards",    "Git"     .
   "shard.lock",  ģ    -   .


______________________________________________
 .

     ,  ,      ,     .     :
$ shards check
   ,   : Dependencies are satisfied (..   ).
     :
$ shards update
___________________________________________


,     ,    . -,        .  ,     "src/mineral_log.cr"  :
 require "katip"


      "Combining Files with Require",        lib.   "shard        .  Katip     ,    , :

LOGGER = Katip::Logger.new

   LOGGER.configure do |config|
   config.loglevel = Katip::LogLevel::DEBUG
   config.logclassification = Katip::LogClassification::DATE_DAY
   config.path = "src/katip/logfiles"
   config.info.description = "This is the Mineral Log project."
   config.info.project = "Mineral Log."
   config.info.version = MineralLog::VERSION # project version
   end


 ,     ,     LOGGER,        "MineralLog"   "Mineral".
 ӣ   ,       -,     ,    "Mineral,   "to_csv",   :

__managing_projects/mineral_log/src/mineral_log.cr__
   module MineralLog
   LOGGER.info("app mineral_log is started!")

   min1 = Mineral.new(101, "gold", "cubic")
   puts min1.to_csv
   end

   class Mineral
   getter id, name
   property crystal_struct
   def initialize(@id : Int32, @name : String, @crystal_struct : String)
   LOGGER.debug("A new mineral is created!")
   end

   def initialize(@id : Int32, logger)
   @name = "rock"
   @crystal_struct = "unknown"
   LOGGER.debug("A new default mineral is created!")
   end

   def to_s
   puts "This is a mineral with id #{id} and is called #{name} "
   puts "It has #{crystal_struct} as crystal structure"
   end

   def to_csv
   LOGGER.debug("to_csv method is called")
   "#{id},#{name},#{crystal_struct}"
   end
   end


 -      "src/katip/logfiles/*.json".         ,  "mineral_log/lib/katip/katipviewer.html"    :
		\.\



 Katip  ,    : "info", "warn"(), "debug", "error"  "fatal"( ),   ,        .

___________________________________________________
  .

  Crystal          -,    .         .          ( )    ,       Crystal (     ).     :

  :      !   ,      ,   "shard.lock"  ,    -  .
        ,      .
     ,    ţ   ,     (       ).
________________________________________________



	_  _

       Crystal-,     
          .       "Benchmark",     ,   .    ,        Ruby.

 "to_s(io)" (  5)  ,      ,  
,   "#{}"  "to_s". ,        ,     .    ,     IO::Memory,  
.  ,   :

__managing_projects/benchmarking.cr__
   require "benchmark"


   IOM = IO::Memory.new


   Benchmark.ips do |x|
   x.report("Appending") do
   append
   IOM.clear
   end


   x.report("Using to_s") do
   to_s
   IOM.clear
   end


   x.report("Interpolation") do
   interpolation
   IOM.clear
   end
   end


   def append
   IOM << 42
   end


   def to_s
   IOM << 42.to_s
   end 


   def interpolation
   IOM << "#{42}"
   end


    "Benchmark",       .     "Benchmark.ips",      .       "do... end"     ,    .    ,       ;   ,         .    "report"     .

     :  
$ crystal build benchmarking.cr --release
     
$ ./benchmarking

    :
   Appending 34.06M ( 29.36ns) ( 3.97%) fastest
   Using to_s 12.67M ( 78.92ns) ( 7.55%) 2.69 x Slower
   Interpolation 2.8M (356.75ns) ( 3.84%) 12.15 x Slower

  ,      .
ݣ     bm,   ޣ   :

__managing_projects/benchmarking.cr__
   Benchmark.bm do |x|
   x.report("Appending bm") do
   IOM.clear
   10_000_000.times do
   append
   end
   end
   end
   user system total real
   Appending bm 0.240000 0.000000 0.240000 ( 0.243686)

  ,      ( , "  ").


  2.
1) ArrayLastElem:     ,     :   "-1"    "last"?
2) Building_vs_Concat:   3 ("  ")  ,       (.. ).    ,   bm.      .



	_  Crystal_

   ,         .  :    !      ,       . ,   "-release"         (    "Compiling Code"),        .




		__
!      (    ),         Crystal  ,     shard.

  ,       ,  ,       , ,     -   Crystal.   III     Crystal:   ,           .






						 3.
						 Crystal-.
       ,       :  , ,         .     -,   Crystal      .  ݣ      Shard'.



						 8.
						  .
						
,      Crystal,     ,       :

     ;
   ,   ,   .   ,   .

    C;
   C,   ,      ,     .    :      C,   .

 :     .    ,   ""    .

    ;
   -   ,  ,           .  Crystal    (  ).

           .     ,     C,            .      ,         Crystal-.



	_DRY    _

   ,  Ruby,       ,      ; ӣ   .   " " ң  "Ruby on Rails",    Ruby  . Crystal    ,      "eval"         (..   Runtime).     ,             .

   ,       ,     , ӣ     .      ?  ,       ,   - .

   ,     .      ݣ  ,   .     ,   .

       ,  .  ,    DRY (Dont Repeat Yourself)!


,  ,   ,     .     Mineral   "name"  "hardness",        "getter":

__advanced_features/macros.cr__
   class Mineral
   def initialize(@name : String, @hardness : Float64)
   end 


   def name
   @name
   end


   def hardness
   @hardness
   end
   end 


   min1 = Mineral.new("gold", 2.5) 
   "#{min1.name} - #{min1.hardness}" # => "gold - 2.5" 


 ,  :      ,    .      ,      get,  ?
 class Mineral 
   def initialize(@name : String, @hardness : Float64) 
   end
   
   get name 
   get hardness 
   end 
###      ,   :
#get name, hardness

   :
1)    name   "get",  :
__advanced_features/macros.cr__
   macro get
   def name
   @name
   end
   end


   class Mineral
   def initialize(@name : String, @hardness : Float64)
   end

   get 

   def hardness
   @hardness
   end 
   end 


   min1 = Mineral.new("gold", 2.5)
   "#{min1.name} - #{min1.hardness}" # => "gold - 2.5" 


  - :   "get"        "name".      .   "def"    "macro".

2)       ,    :
__advanced_features/macros.cr__
   macro get(prop)
   def {{prop}}
   @{{prop}}
   end
   end


   class Mineral 
   def initialize(@name : String, @hardness : Float64)
   end


   get name
   get hardness 
   end 


   min1 = Mineral.new("gold", 2.5)
   "#{min1.name} - #{min1.hardness}" # => "gold - 2.5"


 get   , prop,        .  
    {{}}.          :    {{}}      .      "get"   .


3)       ,   "get name, hardness"?    ,    ,   Splat- *.        "for ... in":

 {% for prop in props %}
    # code
   {% end %}


    :
__advanced_features/macros.cr__
   macro get(*props)
   {% for prop in props %}
    def {{prop}}
   @{{prop}}
   end
   {% end %}
   end 


   class Mineral
   def initialize(@name : String, @hardness : Float64)
   end 


   get name, hardness
   end 


   min1 = Mineral.new("gold", 2.5)
   "#{min1.name} - #{min1.hardness}" # => "gold - 2.5"


 Crystal    ,   getter, setter  property,     .      .    ,    "Object".     "record",      :
__advanced_features/macros.cr__
   record Mineral, name : String, hardness : Float64

   min1 = Mineral.new("gold", 2.5)
   "#{min1.name} - #{min1.hardness}" # => "gold - 2.5"


   ,   ,     DSL (Domain Specific Languages), ..     .


   {% for in %}  ݣ  {% if %} {% else %}. , ,       -.         "type"     @type.

      .     ,  ݣ      .        ,     .

  ?     ,     .       (AST)  ,   ,     AST.
       Crystal,      .  ,    ,      ,      ,     Ruby!


  1.
||>  def_method:   "define_method,     (mname)   ,     .  ,     "greets",   Hi,    "add,   (1 + 2).


______________
 -.

  Ruby   ,        ( Runtime)  ,     .    ,  "method_missing"  .  Crystal   -     ,       :

__advanced_features/macros.cr__
   class Mineral
   getter name, hardness


   def initialize(@name : String, @hardness : Float64)
   end


   macro method_missing(call) 
   print "Unknown method: ", {{call.name.stringify}},
   " with ", {{call.args.size}}, " argument(s): ", 
   {{call.args}}, '\n' 
   end
   end


   min1 = Mineral.new("gold", 2.5)
   min1.alien_planet?(42)
   # => Unknown method: alien_planet? with 1 argument(s): [42]


    "alien_planet?"     Mineral.      :
  undefined method 'alien_planet?' for Mineral 

   "method_missing"         ,   
   ,      .

       ,    :
 inherited:   , @type   ,   (..   ).
 included:   , @type   ,    .
 extended:   , @type   ݣ  (..    ).

  ,     -.      ,   .       .    ,    ,       ,      .   ,   ,  .                .



	_      C_

   Ruby, Crystal   ,           . ,       ,   :

__advanced_features/low_level.cr__
   ptr = Pointer(UInt8).malloc(20) # malloc allocates memory
   ptr.value = 42_u8 # 42 here is of type u8
   ptr # => Pointer(UInt8)@0x271dfe0
   ptr.value # => 42
   ptr.class # => Pointer(UInt8)


   # Converting between pointer types with as:
   # Int8* is the same as Pointer(Int8)
   ptr.as(Int8*) # => Pointer(Int8)@0x271dfe0


   n = 42 
   ptr_n = pointerof(n)
   ptr_n.value = 108
   n # => 108

   :    ,  ,    ,  ,        .     ,    .  "Slice(T)"      ( ),    ,    ,   .    "Bytes"    "Slice(UInt8)".

       Crystal,    ,   .         Crystal,       C.         ,    ӣ   C (  Crystal-),      C-. (  ,     : Crystal      C.)


  Crystal      ,   ,    Ruby   ݣ  C-. C     ,   Crystal.      .     ,     C  Crystal:

__advanced_features/c_bindings.cr__
   lib LibC
   fun rand : UInt32
   fun sleep(seconds : UInt32) : UInt32
   fun atoi(str : UInt8*) : Int32
   fun printf(format : UInt8*, ...) : Int32
   fun cos(val : Float64) : Float64
   fun exit(status : Int32) : NoReturn
   end


   LibC.rand # => 1804289383
   LibC.sleep(1_u32) # => wait 1 second
   LibC.atoi("28").class # => Int32


   a = 1
   b = 2
   LibC.printf "%d + %d = %d\n", a, b, a + b # => 1 + 2 = 3
   LibC.cos(1.5) # => 0.0707372016677029


   LibC.exit(0) # => NoReturn
   puts "hello" # this will never be executed


 ,      "lib LibC".  "lib"     C,
    C  , "nmo",    "LibNMO" (     Crystal).        C  "fun cfname".   ,  ,    ,   ,   ,      C.      Crystal   ,   ,  LibC.atoi("28").

    ,  "LibC"       ,     (  ) .      ,  SDL,     ,      @[Link("")],        .


_______________________________________________________
,     .

    ,   "Int8"  "Int64",    ,   "Float32"  "Float64".     "Pointer", , "Pointer(Int32)",      "Int32*".
  "Void"    Crystal   ,   "Nil" (.. ),  "NoReturn"     (..   ).

C- "char"   "UInt8"   Crystal; "LibC::Char"   .  Crystal  "Char"    4 ,    C    "Int32".  ,    C ( char*  const char*)  Crystal   "UInt8*".
______________________________________________________


__advanced_features/c_libsdl.cr__
   @[Link("SDL")]
   lib LibSDL
   INIT_TIMER = 0x00000001_u32
   INIT_AUDIO = 0x00000010_u32


   fun init = SDL_Init(flags : UInt32) : Int32
   end

   value = LibSDL.init(LibSDL::INIT_TIMER) # => 0


   @[Link("SDL")]   "-lSDL" .   ,   
     Crystal;   "init"  "SDL_Init".    ,       (..   ); "greet.c"   "greet",   "!"  .

__advanced_features/greet.c__
   #include <stdio.h>

   void greet(const char* name){ 
   printf("Hello %s!\n", name); 
   } 

   C    "greet.o",   :
$ gcc -c greet.c -o greet.o
       ,      ,  :

__advanced_features/greet.cr__
   @[Link(ldflags: "#{__DIR__}/greet.o")] 

   lib LibSay
   fun greet(name : LibC::Char*) : Void 
   end

   LibSay.greet("Ary") # => Hello Ary! 

     ,  :
$ crystal build greet.cr
$ ./greet
  .

  C-    ,       £,         .    (.)       :
https://crystal-lang.org/docs/syntax_and_semantics/c_bindings/


       Crystal,     C    , Crystal          "system",  ,  ,   ,    ,  "gedit"   "test.txt"  :
	file = "test.txt"
	system("gedit #{file}")

         ,      .    ,   ģ .




	_ - _
	
  Crystal     CSP- (  ),    Go:  ,  ,     .    ,     ,   Node.js,  .         .  ,     ,       Crystal .


     Crystal?

    ,    (   main)       ( ,   " ").        ,    .


   Crystal  ,    ,     :
1)               . ,      (   /),    :
        ,    -      .       .

2)    (Event Loop)     .    ,    /    (,  ,   ,    ). Event Loop       ,        .

3)   (Garbage Collector)  ,     .  2019     "Boehm GC" (  "-").


     Crystal    ,       .        , " "   ,        .
      /.          ,        .

      4 ,      8        .  ,     ,   Σ     "".  64-     .   32-      512 .


ӣ   (..    ),   (GC),    ,   .      (eBook)  Crystal      ,    (   "Go")    .        Crystal,    .

   ,            .     ,    ,         ,    "Node.js"  JavaScript.   ,      ,       .



   .

,        "spawn do [- ...] end",
       .   ,    ?
__advanced_features/main_fiber1.cr__
   puts "Before start fiber"
   spawn do
   puts "Hello from within fiber"
   end
   puts "After fiber"

     (  ):
	Before start fiber
	 After fiber

 ?   (main) ,   ,       ,  .    ,        .          ϣ  .    ,   "sleep 1.second"   .        ,       .  ( )    "Fiber.yield",         .

            .             .

___________________________________________________
   .

     .          !
       .  : "       !"
_________________________________________________

   ?     Channel(T) , , Channel   .           . ,    [https://hackernoon.com/crystal-the-ruby-youve-never-heard-of-57bad2efac9c],    Crystal:

		\.\


___________________
   .

        ,    ,            .        ,       .       ,   "main"     42,     .   ?

__advanced_features/main_fiber2.cr__
   ch = Channel(Int32).new

   spawn do
   puts "start fiber"
   n = ch.receive # fiber will block here if nothing on the channel yet
   puts "fiber received #{n}"
   end

   puts "before send"		(1)
   ch.send 42 
   puts "main has send 42" 	(4)

   # =>
(1) before send
(2) start fiber
(3)  fiber received 42
(4)  main has send 42

   :

   (1),    ݣ  .    "main"  ,        .  ,         (2),   "42"   (3),    .   "main"   ,     (4).

 , ,      ,    ,       .         ,     ,         .
    (  "nil")      .


  2.
1) "main_fiber3":       ,  "main" ( )  .      .

2) "main_fiber4":   "main"    1  10  ,     
  .       (.. main  ,    ).


_________________________
    .

         :      ,  "main" ģ   (..    ):

__advanced_features/main_fiber5.cr__
   ch = Channel(String).new
   spawn do
   while line = gets
   ch.send(line) 
   end
   end

   puts ch.receive


   # For example: 
   # => 
   # hello 
   # hello


(       .)
     /, ,          .


_______________________________
 ,  .

          ,      ,  : "spawn method1 argumentlist". ,  "spawn"   .    :

__advanced_features/spawn_method.cr__
   def pname(name)
   3.times do |i|
   puts "#{name} - #{i}"
   end
   end

   spawn pname "spawned" # started on another fiber (in background)
   pname("normal") # started by main fiber
   Fiber.yield 
   # =>
   # normal - 0
   # normal - 1
   # normal - 2

   # spawned - 0
   # spawned - 1
   # spawned - 2


 "pname"     ,    "main".


________________________
  .

      ,        ,    ,     ("main"):

__advanced_features/synchronizing.cr__
   # # Synchronization of channels:
   # background worker signals on channel when it is done 
   # main fiber only continues when that signal is received 
   def worker(done : Channel(Bool)) 
   puts "worker: working"
   sleep 2
   puts "worker: done"
   done.send true
   end
  
 
   done = Channel(Bool).new
   spawn worker(done)
  
 
   done.receive # main blocks here
   puts "main: next"
  
 
   # =>
   # worker: working
   # worker: done
   # main: next


____________________________
  .

,      ,       .     (/ɣ)    .  ݣ   ,     ,    :

__advanced_features/buffered_channel.cr__
   ch = Channel(Char).new(2) # A buffered channel of capacity 2


   spawn do
   puts "Before send 1"
   ch.send('\u03B1')
   puts "Before send 2"
   ch.send('\u03B2')
   if ch.empty?
   puts "Channel is empty"
   else
   puts "Channel is not empty"
   end
   puts "Before send 3"
   ch.send('\u03C9')
   puts "After send"
   end
   3.times do |i|
   puts ch.receive
   end


   # => 
   # Before send 1
   # Before send 2
   # Channel is not empty
   # Before send 3
   #""
   #""
 # After send
   #""


      ģ      .   "" ""     .  ,     ,     "empty?".   ,      ,        "closed?".


____________________
  .

,    ,     -  ,       .  Crystal   "select when",    :
__advanced_features/channel_select.cr__
   def generator(n : T) forall T
   chan = Channel(T).new
   spawn do
   loop do
   sleep n
   chan.send n
   end
   end
   chan
   end


   ch1 = generator(1) 
   ch2 = generator(1.5) 
   ch3 = generator(5) 


   loop do
   select
   when n1 = ch1.receive
   puts "Int: #{n1}"
   when f1 = ch2.receive
   puts "Float: #{f1}"
   when ch3.receive
   break
   end
   end


   # Output:
   # Int: 1
   # Float: 1.5
   # Int: 1
   # Float: 1.5
   # Int: 1
   # Int: 1
   # Float: 1.5


   ,    .      -  (  ),       .     
 .       ,    ("float"),     "generator".          "forall T",    .
  "main"          "select when".    "break"   ,   "ch3"   .



_______________________
   .

          .    ,        ?

__advanced_features/lines_files.cr__
   ch = Channel(Int32).new
   total_lines = 0
   files = Dir.glob("*.txt")

   files.each do |f|
   spawn do
   lines = File.read_lines(f).size
   ch.send lines
   end
   end

   files.size.times do
   total_lines += ch.receive
   end


   puts "Total number of lines in text files: #{total_lines}"
   # => Total number of lines in text files: 7


, , :  "Dir.glob"         ,   ,        ".txt".        ,      ;   , ,     .

,      .   "main"    (      ),           .

     :
    .
     .
    (main)    .

        :  .



	_   _

Crystal       SQL  NoSQL.    SQLite, MySQL ( MariaDB), Postgres, MongoDB, Redis,  ReThinkDB.
 Crystal         ,    -,    "crystal-db",     API   . : "crystal-db"       SQLite, MySQL  "Postgres".       -.        .


     ,     SQLite, chinook.db. 
        "artists"  "albums"  .    "crystal-db",      ,  ,       .
   Crystal (  "crchinook")    
"$ crystal init app crchinook".
    "crystal-sqlite3",   shard.yml  :
	dependencies: 
	sqlite3: 
	github: crystal-lang/crystal-sqlite3


   "$ shards",    .     
  "src/crchinook.cr".     require "sqlite3" .    
  ,       :

__advanced_features/crchinook/src/crchinook.cr__
   require "./crchinook/*"
   require "sqlite3"			##
  
 
   DB.open "sqlite3://../chinook.db" do |db|
  sql = "SELECT artistid, name FROM artists ORDER BY name ASC;"		#(1)
   db.query sql do |rs| 
   p "#{rs.column_name(1)} (#{rs.column_name(0)})" 
   rs.each do # perform for each row in the ResultSet
   artistid = rs.read(Int32)
   name = rs.read(String)
   p "#{name} (#{artistid})"
   # => Name (ArtistId)
   # => A Cor Do Som (43)
   # => AC/DC (1)
   # => Aaron Copland & London Symphony Orchestra (230)
   # => ... 
   end
   end
  sql = "SELECT name FROM artists WHERE artistid = 231;"		#(2)
   p db.query_one sql, as: String
   # => "Ton Koopman"
  sql = "SELECT MIN(birthdate) FROM employees;"		#(3)
   oldest = db.scalar sql # => "1947-09-19 00:00:00"
sql = "SELECT firstname, lastname FROM employees WHERE birthdate = ?;"	#(4)
   firstname, lastname = db.query_one sql, oldest, as: {String, String} 
   p "#{firstname} #{lastname}" # => "Margaret Park"
db.exec "insert into artists values (?, ?)", 276, "Scott Ross"		#(5)
   args = [] of DB::Any
   args << 277
   args << "Bernard Foccroules" 
   db.exec "insert into artists values (?, ?)", args
sql = "SELECT name FROM sqlite_master WHERE type='table';"		#(6)
   db.query_all(sql, as: String) 
   # =>
   # [ "albums",
   # "sqlite_sequence",
   # "artists",
   # "customers",
   # "employees",
   # ..., 
   # "sqlite_stat1"
   # ] 
   end


1)     .
2)     "artistid (, ID).
3)   ,   .
4)      .
5)   "exec"  DDL ( "Data Definition",  ).
6)     .


     "$ crystal build src/crchinook.cr"      "$ ./crchinook".
    ,  "DB.open"   "URI"   .       :
  sqlite3:///path/to/data.db
  mysql://user:password@server:port/database
  postgres://server:port/database


  "DB.open"   "do end",         "end".    ,     :
 db = DB.open "sqlite3://../chinook.db"
   begin
   # ... use db to perform queries
   ensure
   db.close
   end

 , "db" ( "DB.open")   "DB::Database",                  .     ,    ,  ,  ,     .


____________________________________________________
   .

      URI,    ,     "db.uri.scheme.  ,     SQL     .   "case...when",   :
 sql = case db.uri.scheme
   when "postgres"
   # SQL for postgres
   when "mysql"
   # SQL for mysql
   when "sqlite3"
   # SQL for sqlite3
   else
   raise "SQL not implemented for #{db.uri.scheme}"
   end
__________________________________________________


      "query",    "ResultSet".      ,   "each"       .      ,   "rs.read(Int32)",      
     .    
  "DB::Types"    : Nil, String, Bool, Int32, Int64, Float32, Float64, Time, Bytes.
 "DB::Any"      ,        .

      ,   "rs.column_name(1)",     
  "column_names".      : "artistid, name = rs.read(Int32, 
String)".  "query_one"    , "scalar"      ,      "query_all" Σ  "as: Type".

 (4)  (5)   "crchinook" ,      SQL-,      "?",   SQLite.  Postgres    "$1"  "$2".          .     SQL    SQL-.


 ,       (, , ),   "exec",    (5).  ,              "?",   -.

   (6)   "query_all"        ,    ,         ;
query_all     ,       
   .  [http://crystal-lang.github.io/crystal-db/api/latest/DB/QueryMethods.html]          .

 SQL,    ,      .



_____________________________________________________________
      .

       ,    .              
begin
 
 rescue
 
 ensure
 
end
____________________________________________________________


         (..    ),   ORM (- )   . Crystal   ,         [https://github.com/veelenga/awesome-crystal#ormodm-extensions]    [https://crystalshards.org/?filter=orm].

    :
 , [https://github.com/topaz-crystal/topaz]    £ .
 Crecto, [https://github.com/Crecto/crecto]   Ecto   Elixir,       .       .
 Granite-orm, [https://github.com/amberframework/granite-orm]    - Amber,    Postgres, MySQL  SQLite.

   ݣ         ,   [http://crystal-lang.github.io/crystal-db/api/latest/].




	 ...

      ,        .  ,    -    Crystal.  ݣ     :       .    ,   ,          . ݣ  ,          .

  ( )   ,  Crystal    -.
     -,     .







							 9.
							 - & Shards.

      Crystal  .     Crystal,   ,      ,      .
         -  Crystal   Kemal.           ,     -,       .       !


	_ -    Kemal  Amber_

  Ruby     Rails,  -.    Crystal    , ,        -.    ,    Crystal,  ,      Kemal.


 -.

  Crystal  HTTP-   -,     ,  ,  ,  , -  ..     -,  "HTTP::Server".   ""              -:

__web_frameworks_and_shards/web_server.cr__
   require "http/server"

server = HTTP::Server.new do |ctx|				(1)
ctx.response.content_type = "text/plain"				(2)
ctx.response.print "Crystal web server: got #{ctx.request.path}"	(3)
   end 


   server.bind_tcp 8080
server.listen							(4)
   puts "Crystal web server listening on http://localhost:8080"


   # => in browser with the URL http://localhost:8080/
   # "Crystal web server: got /"

   ,      "require"    HTTP-.     "Server"   (1).       ,   -    .         "do ... end".    (1)   "ctx" (  "Context"),     ; , "path"  ,  .        HTTP- (.  [https://crystal-lang.org/api/latest/HTTP.html]).

        ,  "content-type" (  )   (2).
    ,   "text/plain".         "status"  "header".     ( "print")  ,   (3).    - (, ),       .

    IP-      bind_tcp,      "listen"   (4). 
  "true"      "bind_tcp", -    .  ,               ,     .

      ,   ,       :
ip = "0.0.0.0"
   port = 8080
   server.bind_tcp ip, port # ...

    "$ crystal web_server.cr".    :
	 Crystal web server listening on http://localhost:8080 
    .
     ,    :
	"  Crystal web server: got /  "


     "CTRL+C"   "close"   .

  -    ,       -   ,      . ,      .    ,    .
 "router_cr" [https://github.com/tbrand/router.cr]        - Crystal.

     ,    http-.   ,  - Crystal     ,    (.  "Speeding Up the Web"     Web-).



  1.

1) XML- :  -,    5000,        .     XML. (:    text/xml.)

2)   -:  -,    ,      "index.html",    URL- "http://127.0.0.1:3000/public".
(:     (Handlers),         "IO.copy").



 - "Kemal".

Kemal [http://kemalcr.com/]    -,   ,  -     .       Frank,   Crystal.

Kemal   .      Sinatra ( -  Ruby),         .        (+- 1MB)  :       .


______________________________
  Kemal-.

         1.
    :
     "crystal init" (  "simple_kemal_app").
      "shard.yml":
	dependencies:
	kemal:
	github: kemalcr/kemal

   "shards".
  , ,  "kemal", ݣ   - ("radix",  
,  "kilt",    )  :
	Installing kemal
	Installing radix (0.3.8)
	Installing kilt (0.4.0)

 :         "/lib",    100% Crystal-,      ,  ӣ  !


   "src/simple_kemal_app.cr"  :

__web_frameworks_and_shards/simple_kemal_app/src/simple_kemal_app.cr__
require "./simple_kemal_app/*" 
require "kemal"				(1)


get "/" do					(2)
"My first Kemal app is alive!" 
end

Kemal.run				(3)



      "crystal src/simple_kemal_app.cr" (  "crystal build"    ).   :
	" [development] Kemal is ready to lead at http://0.0.0.0:3000 "


    "http://localhost:3000/" (   - 3000)    :   Kemal- !
 ,     - :

2017-10-04 10:10:17 +0200 200 GET / 165.0s
2017-10-04 10:10:17 +0200 404 GET /favicon.ico 204.0s
   ... 
^CKemal is going to take a rest! 


    (  )  ,  "logging false".  ,        ,  .    ,   , ( 8080,  ),   "Kemal.config.port = 8080".

        :
(1)
 (require)  .
(2)
   (GET http ://localhost: 3000/)    ,  Σ .
(3)
 -,    "run".

  ,        -,     :
   get "/" do |env|
   env.redirect "index.html"
   end

 "env"             , 
 (get "/" do)      (get "/" do |env|).  "env"  ,            (  URL, ,     ),     ,   . ݣ "env"    "set"  "get",          .
___________
:
*     <form> HTML-,   -    .


      HTTP,   GET, POST, PUT, PATCH  DELETE:
       "do end",     .        RESTful (ReST    )  -.
   (  "     "):   ,    URL,  .



________________________________________
     ECR.

    -,     .    ,     , "Kemal"    ECR (Embedded Crystal) [https://crystal-lang.org/api/latest/ECR.html].

     ,     Crystal    HTML,    ERB   Ruby. ,    ERB,       ,  - :             .  ECR   -.

     .      "appviews".          "sentry" [https://github.com/samueleaton/sentry],
  ,    (Samuel Eaton).

  -      ,     .  ,          !       ,  :

$ curl -fsSLo- https://raw.githubusercontent.com/samueleaton/sentry/master/install.cr | crystal eval



( ,    "crystal eval",   "install.cr".)
   ,   "$ ./sentry".   :

	\.\


  "sentry"     -   ,   ".gitignore" :
  /doc/
   /lib/
   /bin/
   /.shards/
   /dev/
   sentry
   appviews


         .       "src/appviews.cr":  "sentry"  ,   ,      Kemal  !

 , ,     ,   JavaScript   .    "Bootstrap"  ( -     ),  ݣ        "appviews.css".       "public"  :
	appviews/
	src/
   appviews.cr
   public/
   images/ 
   fonts/ 
   js/ 
   bootstrap.js 
   appviews.js 
   css/ 
   bootstrap.css 
   appviews.css 
   index.html

   ӣ      "public",     ,
     "/js/appviews.js".      ,    ,  "serve_static false"   .
    ,  : "public_folder src/public".          "index.html".

       ,  ECR.    ,   "get"  "src/appviews.cr"   :
  get "/" do
   title = "Home"
   page_message = "Your app's home page"
   render "src/views/home.ecr"
   end

   : "title"  "page_message".   ,   ECR:
   ("render")  ".ecr",     "src/views",     - (   ).    ,     ECR.      "home.ecr",   :
 <div class="jumbotron">
   <h2><%= page_message %></h2>
   <p>Your home page text starts here ...</p>
   <p><a class="btn btn-lg btn-primary" href="#" role="button">Learn
   more &raquo;</a></p>
   </div>


 "sentry" ,    (  "http ://localhost: 3000")   :

		\.\



  ?  "<%= page_message %>"  "page_message"     "home",      "<% =% >". ݣ     Crystal  "<% %>",    .   ,  ,         (  ).

     ".ecr"     "layouts" ( src/views/).  "main_layout.ecr"   .  ,  HTML-,       (,       ,     eBook).

   ,       :
	render "src/views/home.ecr", "src/views/layouts/main_layout.ecr"

  ?     -  "<%= title %>";     :
 <div class="container">
   <%= content %>
   </div>

 "home.ecr"    ,      "content" 
    .

        JavaScript,    HTML-     (CSS).    "content_for"  "yield_content"   ;        Kemal [http://kemalcr.com/guide/].

    ,    "<div> navbar":

  <li <% if env.request.path == "/" %>class="active"<% end %>>
   <a href="/">Home</a></li>
   <li <% if env.request.path == "/about" %>class="active"<% end %>>
   <a href="/about">About</a></li>
   <li <% if env.request.path == "/contact" %>class="active"<% end %>>
   <a href="/about">Contact</a></li>


 , <% if [condition] %>[effect]<% end %>,         "env",    CSS,   .       ,         :

	(\.\)



   "about.ecr"  "contact.ecr"   "views      "get"
 "appviews.cr".          :
  get "/about" do |env|
   title = "About" 
   page_message = "Your app's description page" 
   render "src/views/about.ecr", "src/views/layouts/main_layout.ecr" 
   end

   get "/contact" do |env| 
   title = "Contact" 
   page_message = "Your app's contact page" 
   render "src/views/contact.ecr", "src/views/layouts/main_layout.ecr" 
   end 

 (Register  )  (Login    ),  Kemal  ,      .      ,  -!


_________________________________________
      JSON.

     ,   HTML-.        JSON    ;      
[https://manas.tech/blog/2017/01/16/from-a-db-to-json-with-crystal.html]  .


  ,     "   ",       sqlite3-, "chinook.db".     :
 ,  "db_json",   - "kemal"  "sqlite3".

     :
     .
    GET:
	1)  "/"(.. "root"    )     .
	2)   "/:table_name",  "/artists",      JSON.
  - Kemal.
    .

    .        "env.response.content_type".    .  SQL-   "query_all".    ,       "to_json".     JSON-   .   ӣ!

   get "/" do |env|
   env.response.content_type = "application/json"
   tables = table_names(db)
   tables.to_json
   end


   def table_names(db)
   sql = "SELECT name FROM sqlite_master WHERE type='table';"
   db.query_all(sql, as: String)
   end


    :


	\.\



        HTML-,   :

   get "/" do |env|
   tables = table_names(db)
   render "src/views/tables.ecr"
   end

   "tables.ecr",    "tables":
   <header>
   <h1>All tables</h1>
   </header>
   <body>
   <% if tables %>
   <% tables.each do |table| %>
   <p><%= table %></p>
   <% end %>
   <% end %>
   </body>


   ,    ( "tables")      ,      .

	\.\


   .

      URL.     "env.params.url",     (1)  .   SQL-,    (2) ,      .      (3)         (4).   JSON-   "write_json" (5).

__web_frameworks_and_shards/db_json/src/db_json.cr__
   get "/:table_name" do |env|
   env.response.content_type = "application/json"
 table_name = env.params.url["table_name"]		(1)
   # avoid SQL injection by checking table name
unless table_names(db).includes?(table_name)		(2)
   # ignore if the requested table does not exist.
   env.response.status_code = 404
   else
db.query "select * from #{table_name}" do |rs|		(3)
   col_names = rs.column_names
rs.each do						(4)
write_json(env.response.output, col_names, rs)		(5)
   # force chunked response even on small tables
env.response.output.flush				(6)
   end
   end
   end
   end


          ,    .         .

     "flush"      (6),     JSON   ;       .
 JSON    "write_json",     :

__web_frameworks_and_shards/db_json/src/db_json.cr__
   def write_json(io, col_names, rs)
JSON.build(io) do |json|			(1)
   json.object do
   col_names.each do |col|
json_encode_field json, col, rs.read		(2)
   end
   end
   end
   io << "\n"
   end


   JSON          ,        JSON [https://crystal-lang.org/api/latest/JSON.html].

"Crystal-db"             , 
        . (,          ,    .)    "JSON:: Builder"   (1)   ,     "io"      -,    (5)  .

    ,  JSON-      "": {"ArtistId":1,"Name":"AC/DC"}.
, "json.object"     JSON        "do".      "col"    "rs.read".  ,     "json_encode_field",   (2):

__web_frameworks_and_shards/db_json/src/db_json.cr__
   def json_encode_field(json, col, value)		#
   case value
  when Bytes				(1)
   json.field col do
   json.array do # writes begin and end of an array
   value.each do |e|
   json.scalar e # writes the value
   end
   end
   end
   else
   json.field col do # write an object's field and value
  value.to_json(json)				(2)
   end


     "to_json"        (2),    "Bytes"   (1)    ,          .      ,       ,      (. - Binary Linked Object).

       "db_json/src/db_json.cr" (..     ).
         "artists":

		\.\



_______________________________________________
    -.

   ,      ,        ECR   ,    -,     .        Crystal  .  ݣ   Kemal-,    :

   ToDo ( . -) [https://github.com/Angarsk8/realtime-todo-app],    Kemal, React  PostgreSQL,   -.
     [https://github.com/Angarsk8/chat-app-demo] . [http://kemal-react-chat.herokuapp.com/ ]

  [76],   JWT  "auth0",   .[77]
   . [78]
     Kamber. [https://github.com/f/kamber]

 Kemal-watcher [80]       ,     "sentry".
 ,      SMS- [81]     [82]   Twilio API.



      .     ݣ    ģ:

     ;    "/users/108",     get "/users/:id",   108   "env.params.url["id"]".

      ,      ,    .    "before_x"  "after_x",  "x"  : "get", "post", "put", "patch", "delete"  "all".

  Kemal     (  ),  Rack:  ,    "Kemal::Handler"          .     ,  ,      .           "csrf".

      ,   "kemal-session"[83].     , ,   Redis    MySQL.

 Kemal    - (   , ),    ,  Node.js. (,     -.)
     -    "spec-kemal".

 Kemal     cookie,  ,   SSL,       ,   ,     "smtp.cr"[84],    .

    ϣ     Heroku     Capistrano.

 - [85],     ,     . Raze [86]     ,     Kemal.



AMBER  -,   RAILS.

     -,        .     ,       Ruby on Rails, Crystal   Rails- .  ,        .

Amber [https://amberframework.org/]
   :
 Kemal, Ruby-on-Rails, Phoenix     ;     MVC (Model-View-Controller).        [https://github.com/amberframework/amber],   ,   Amber    .       Kemalyst [89],     .

 Amber    Rails  Crystal. Amber     MVC-,         -    .

     ORM (- Mapper),     [https://github.com/amberframework/granite-orm]     ,   . Amber    ,   "find_by"  "all",      "belong_to"  "has_many".      ORM Crecto (  "ecto"  Phoenix)    ORM Jennifer.

Amber    ,  (-) "amber".       ,   ,   .      ,   Phoenix.

        PostgreSQL,       MySQL, SQLite       .      Slang,     ECR. Amber   Sentry    .   HTTP-,  Amber    -      .
          Amber,  . [91]


   ݣ    :  [92], carbon-crystal[93],  Lucky[94]   Thoughtbot.      lattice-core (  "websocket-first") [95],    Kemal      "-" (  -).




	_    _

   :     eBook      3700  -  .    ,      :

     Libhunt [96],      50  .
      Awesome Crystal.[97]
 Crystal Shards [98]    ,    ,      .
 Crystal ANN [99]   ,    , ,      Crystal.


           -.      WEB,            .
 c  ,   ,     DSL-
 Crystal.

  .
 Sidekiq [100]         ,     ,   API-    Ruby.[101]
Sidekiq  Kemal        -  .
 Schedule [102]  Cron_scheduler [103]    .


 .
 Baked File System [104]          ,   .

    .
    "Qt5.cr" [105],      "Qt5",  "libui" [106],      - GUI  Linux, OS X  Windows.
  crystal-gl  Mac OS X  Linux [https://github.com/ggiraldez/crystal-gl]    OpenGL   GLFW, SOIL, GLM  .


 .
        CrSFML [108].
 Cray [109]       - RayLib  , 2D  3D-, ;  : /// ,     .
 Glove [110]    ,    .


     .
 Crystal-fann [111]          Crystal; FANN   NeuraLegion    .
 Ai4cr [112]   Ruby Playground,    .


  .
 crystal-futures [https://github.com/dhruvrajvanshi/crystal-futures]      "Future",  ,    JavaScript. Futures       ,        .
 Crystal-clear [https://github.com/Groogy/crystal-clear]   "  ",  .


    API.
 Twitter-crystal [115]       API .
  ݣ    "Salesforce REST API",    Spotify, Google Maps, Docker, GitHub, Slack, SoundCloud   .



______________
   ...

      Crystal    :   http-, ̣   - Kemal , ,   -.         .
        .  ,      ,     ţ.        ,  Crystal          , ,        .          .  ,      .
