2016-05-11 17 views
5

Sono nuovo in Ruby e cercando di capire questa sintassi:Come funziona la sintassi del blocco di Ruby?

create_table :posts do |t| 
    t.string :title 
    t.string :content 
    t.string :likes 
    t.string :comments 

    t.timestamps null: false 
end 

Capisco perfettamente cosa questo codice sta facendo, ma non capisco come funziona. Più specificamente, capisco che create_table è un metodo e :posts è un parametro, ma non capisco il resto del codice.

+1

Hai letto http://guides.rubyonrails.org/active_record_migrations.html#creating-a-table? Se è così, forse puoi chiedere delle cose che non sono già trattate lì. –

+0

Utilizzare la punteggiatura inglese quando si scrive in inglese. – sawa

+0

@sawa mi dispiace per il mio cattivo inglese –

risposta

3

Prima di tutto dimentichiamo Active Record e ci concentriamo sulla struttura del codice stesso. Ecco una versione super semplice di quella struttura.

class MyBuilder 
    def initialize 
    # keys are property names, values are options 
    @properties = {} 
    end 

    def property(name, options={}) 
    @properties[name] = options 
    end 

    def build 
    # For simplicity, just return all properties 
    @properties 
    end 
end 

def create_thing(name) 
    puts "Begin creating #{name}" 

    builder = MyBuilder.new 

    puts "Let user use the builder to define properties" 
    yield builder 

    puts "Consume the builder" 
    properties = builder.build 

    puts "Persist changes to #{name}..." 
    # For simplicity just print them out 
    p properties 

    puts 'done' 
end 

create_thing :bar do |builder| 
    builder.property :counter, color: 'brown' 
    builder.property :wine_storage, texture: 'wood' 
end 

Si prega di digitare il codice sopra a mano per afferrare qualche sensazione.

Sebbene il codice sopra non abbia nulla a che fare con Active Record, ha la stessa struttura della migrazione.

Quando mai create_table viene chiamato, istanzia un costruttore (di tipo TableDefinition), e "spinge" che builder al blocco (da yield dola) per permettere all'utente di definire le colonne tabelle. Il builder viene consumato in un secondo momento da create_table quando l'utente ha finito di definire le colonne.

+1

potrebbe usare un altro nome di variabile all'interno dell'ultimo blocco (ad esempio 'b') per evidenziare che è indipendente dal nome della variabile resa. – Stefan

+0

Grazie a @Etherus questo è quello che sto cercando :) –

8

Brace per esso :)

  1. create_table è un metodo. :posts è un simbolo che viene passato come parametro. Le parentesi sono opzionali, quindi sembra strano ma è una semplice chiamata di metodo.

  2. Tutto tra do e end è un blocco di codice. È uno dei tanti modi per passare il codice come argomento a un metodo. I blocchi di codice sono grandi per il caso comune. Altri modi simili (ma diversi) per farlo è usare Proc o lambda o ->.

  3. |t| è un argomento trasmesso nel blocco di codice da create_table. create_table eseguirà il blocco di codice e passerà un oggetto tabella come argomento singolo ad esso. Hai scelto di nominare quell'oggetto t.

  4. Ora all'interno del blocco di codice si chiama il metodo string sull'oggetto t e lo si passa come argomento. Lo stai facendo quattro volte. Nuovamente le parentesi sono opzionali.

  5. Si sta chiamando il metodo timestamps sullo stesso oggetto t. Qui stai passando un parametro, che è un hash con il valore { :null => false }.

    • Non solo le parentesi sono facoltative, ma anche le parentesi graffe sono facoltative quando si passa hash come ultimo o solo parametro a un metodo.
    • null: false, è una sintassi di scelta rapida per { :null => false }.

Così tutto quanto sopra è equivalente a:

create_table(:posts) do |t| 
    t.string(:title) 
    t.string(:content) 
    t.string(:likes) 
    t.string(:comments) 
    t.timestamps({:null => false}) 
end 
+0

"null: false, è una scorciatoia" anche se una scorciatoia, non è progettata come una, è semplicemente la più recente sintassi ruby ​​2.x per hash –

+0

@Koen. Tecnicamente 1.9 e il supporto più recente, quindi è stato in giro per un po '. Ci vuole un po 'di tempo per abituarsi se si sta ancora suonando lo stile 1.8 ma è molto meno complicato. – tadman

+1

@Koen. non del tutto. È una sintassi di scelta rapida per gli hash quando la chiave è un simbolo. Non è possibile creare un hash in cui la chiave non è un simbolo (ad esempio stringa, numero, oggetto con la sintassi "più recente".) Quindi è ancora uno zucchero sintattico – quarterdome

2

Una delle lotte con l'apprendimento Ruby è che, come Smalltalk et al, si arriva a passare il codice in giro così come i dati . Un modo in cui è possibile passare il codice a un metodo è con un blocco di codice. È quindi possibile chiamare il blocco di codice nella definizione del metodo con yield che dice "inserire questo blocco di codice al posto di yield":

def do_it 
    yield 
end 

do_it { 2 + 4 } 
=> 6 

Vedrete anche di inviare i parametri nel blocco di codice dalla definizione del metodo. Ecco dove | t | entra:

def do_it_with_ten 
    yield 10 
end 

do_it_with_ten { |t| (2 + 4) * t } 
=> 60 

Si noti che le parentesi graffe sono equivalenti a do..end.

Sto indovinando che questo è il codice che avete trovato con resa in esso:

def create_table(name, options = {}) 
    table_definition = TableDefinition.new(self) 
    table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false 

    yield table_definition 

    if options[:force] 
    drop_table(name) rescue nil 
    end 

    create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE " 
    create_sql << "#{name} (" 
    create_sql << table_definition.to_sql 
    create_sql << ") #{options[:options]}" 
    execute create_sql 
end 

Questo è esattamente quello che stai cercando. Questa è la definizione del metodo create_table che stiamo chiamando. Puoi vedere la resa con il parametro table_definition.