variabel lokal opsional di templat parsial rel: bagaimana cara keluar dari kekacauan (didefinisikan? foo)?

225

Saya sudah menjadi anak yang buruk dan menggunakan sintaks berikut di templat parsial saya untuk menetapkan nilai default untuk variabel lokal jika nilai tidak didefinisikan secara eksplisit di: hash lokal saat rendering parsial -

<% foo = default_value unless (defined? foo) %>

Ini tampaknya berfungsi dengan baik sampai saat ini, ketika (tanpa alasan saya bisa membedakan) variabel-variabel yang tidak lulus mulai berperilaku seolah-olah mereka telah didefinisikan nihil (daripada tidak terdefinisi).

Seperti yang telah ditunjukkan oleh berbagai orang yang membantu di SO, http://api.rubyonrails.org/classes/ActionView/Base.html mengatakan tidak menggunakan

defined? foo

dan bukannya digunakan

local_assigns.has_key? :foo

Saya mencoba mengubah cara saya, tetapi itu berarti mengubah banyak template.

Bisakah / haruskah saya mengisi daya ke depan dan membuat perubahan di semua templat? Apakah ada trik yang harus saya perhatikan? Seberapa rajin saya perlu menguji masing-masing?

brahn
sumber

Jawaban:

324

Saya melakukan ini:

<% some_local = default_value if local_assigns[:some_local].nil? %>
jonnii
sumber
1
Meskipun saya benar-benar menyukai sintaks kompak dari saran hgimenez (di atas), pendekatan ini memiliki keuntungan menjadi sangat jelas kembali: apa yang terjadi. (Masih memiliki sisi negatif dari tidak membiarkan Anda lulus nol sebagai nilai untuk lokal)
brahn
1
Apa gunanya Anda ingin lulus nol?
Jonnii
Oh, saya tidak punya kasus khusus dalam pikiran. Hanya berusaha memahami implikasi penuh. Ini mengingatkan saya untuk menerima jawaban ini :-)
brahn
4
Untuk mengatasi masalah nil, saya menyalin kode dari tautan OP ( api.rubyonrails.org/classes/ActionView/Base.html ) <% jika local_assigns.has_key? : headline%> Headline: <% = headline%> <% end%> - has_key menghindari situasi nihil / salah, dan mungkin dapat disingkat menjadi satu baris seperti jawabannya di sini
Phil
5
Silakan periksa jawaban Pablo: dengan local_assigns.fetchbenar menangani kunci genap dengan nilai nol. Ini mengembalikan default hanya jika kunci tidak disetel sama sekali.
quetzalcoatl
158

Karena local_assignshash, Anda juga bisa menggunakan fetch dengan opsional default_value.

local_assigns.fetch :foo, default_value

Ini akan kembali default_valuejika footidak disetel.

PERINGATAN:

Hati-hati dengan local_assigns.fetch :foo, default_valuekapan default_valuemetode, karena akan dipanggil pula untuk meneruskan hasilnya fetch.

Jika Anda default_valueadalah sebuah metode, Anda dapat membungkusnya dalam sebuah blok: local_assigns.fetch(:foo) { default_value }untuk mencegah panggilannya saat itu tidak diperlukan.

Pablo Cantero
sumber
1
Layak untuk mengatakannya secara eksplisit: nilnilai dipertahankan di sini. Jika hash berisi :foodipetakan ke nil, maka fetchakan kembali nil. Yaitu, setidaknya pada v1.9.3 saya. Saya tidak ingat bagaimana perilaku saya.
quetzalcoatl
Itu sepenuhnya benar. Itu mengingat saya masalah local_assigns[:foo] || default_value, ketika foo mengembalikan nilai falsy, yang default_valueakan digunakan sebagai gantinya. Biasanya masalah untuk Memoisasi @some_value ||= expensive_methodjika metode mengembalikan nilai falsy, itu akan selalu dieksekusi.
Pablo Cantero
1
Siapa pun yang tidak mengerti mengapa ini adalah jawaban terbaik belum cukup lama menggunakan ruby. Bravo Pablo!
mastaBlasta
2
Optimasi adalah sebagai berikut, sehingga Anda hanya perlu memanggil ini sekali di bagian atas template Anda, daripada menggunakan 'fetch guard' pada setiap penggunaan variabel. foo ||= local_assigns[:foo] = local_assigns.fetch(:foo, default_value)
sethcall
Saya bertanya-tanya mengapa itu tidak berhasil, saya berasumsi itu menciptakan variabel juga, tetapi kita masih harus menggunakan nilai yang dikembalikan:foo = local_assigns.fetch :foo, true
Vadorequest
84

Bagaimana tentang

<% foo ||= default_value %>

Ini mengatakan "gunakan foojika tidak nil atau benar. Jika tidak tetapkan default_valueke foo"

hgmnz
sumber
2
Saya tidak yakin ini berfungsi karena foo tidak didefinisikan kecuali itu diteruskan melalui hash lokal.
Jonnii
1
Ini berfungsi, tetapi jika Anda memiliki nilai default seperti ini, mungkin itu pertanda bahwa Anda harus menggunakan helper?
psyho
2
Tidak ada keajaiban di sini. Sumber lebih banyak tentang masalah ini: groups.google.com/group/comp.lang.ruby/browse_thread/thread/…
hgmnz
37
Saya sangat suka versi ini karena sintaksnya sangat ringkas. Saya kira kerugian besar adalah bahwa itu berarti Anda tidak dapat melewati nol atau salah sebagai nilai untuk lokal, karena akan ditimpa secara default.
brahn
16
@ Brahn, itu poin yang bagus. Sebenarnya, ini harus dihindari jika foomerupakan boolean. Mungkin berhak memiliki nilai false, dan ditimpa secara default_valuetidak sengaja.
hgmnz
10

Saya pikir ini harus diulang di sini (dari http://api.rubyonrails.org/classes/ActionView/Base.html ):

Jika Anda perlu mengetahui apakah variabel lokal tertentu telah diberi nilai dalam panggilan render tertentu, Anda perlu menggunakan pola berikut:

<% if local_assigns.has_key? :headline %>
  Headline: <%= headline %>
<% end %>

Pengujian menggunakan didefinisikan? informasi utama tidak akan berfungsi. Ini adalah batasan implementasi.

gamov
sumber
6

Dalam kasus saya, saya menggunakan:

<% variable ||= "" %>

di parsial saya.
Saya tidak tahu kalau itu bagus tapi untuk saya tidak apa-apa

Moises Portillo
sumber
Ini sebenarnya bekerja cukup baik. Bekerja untuk yang tidak terdefinisi dan ketika lewat nilsebagai panggilan parsial lokal juga.
Joshua Pinter
3
Ah, membaca di bawah, ini akan gagal jika variableboolean dan Anda perlu mengaturnya false. Ini akan menggunakan nilai default daripada menggunakan falsenilai. GUNAKAN RISIKO ANDA SENDIRI.
Joshua Pinter
5

Aku tahu itu thread lama tapi di sini kontribusi kecil saya: saya akan menggunakan local_assigns[:foo].presencedalam kondisional dalam parsial. Kemudian saya atur foohanya saat dibutuhkan dalam panggilan render:

<%= render 'path/to/my_partial', always_present_local_var: "bar", foo: "baz" %>

Lihatlah panduan resmi Rails di sini . Berlaku dari RoR 3.1.0.

mikrosino
sumber
Saya tidak bisa melihat perbedaan nyata antara local_assigns[:foo]dan local_assigns[:foo].presence. Salah satu akan kembali niljika kunci tidak ada di hash dan nilainya jika itu ada.
jamesmarkcook
1

Saya pikir opsi yang lebih baik yang memungkinkan untuk beberapa variabel standar:

<% options = local_assigns.reverse_merge(:include_css => true, :include_js => true) %>
<%= include_stylesheets :national_header_css if options[:include_css] %>
<%= include_javascripts :national_header_js if options[:include_js] %>
Daniel OCallaghan
sumber
1

Ini adalah turunan dari jawaban Pablo. Ini memungkinkan saya untuk menetapkan default ('penuh'), dan pada akhirnya, 'mode' diatur dalam local_assigns dan variabel lokal aktual.

haml / ramping:

- mode ||= local_assigns[:mode] = local_assigns.fetch(:mode, 'full')

erb:

<% mode ||= local_assigns[:mode] = local_assigns.fetch(:mode, 'full') %>
sethcall
sumber
0

Lebih intuitif dan ringkas:

<% some_local = default_value unless local_assigns[:some_local] %>

muirbot
sumber
3
Saya pikir ini akan gagal jika Anda memanggil parsial dengan:locals => {:some_local => false}
brahn
0

Jika Anda tidak ingin meneruskan variabel lokal ke parsial setiap kali Anda menyebutnya, lakukan ini:

<% local_param = defined?(local_param) ? local_param : nil %>

Dengan cara ini Anda menghindari undefined variablekesalahan. Ini akan memungkinkan Anda untuk memanggil parsial Anda dengan / tanpa variabel lokal.

Haris Krajina
sumber
ATAU local_param = local_param jika didefinisikan? (Local_param)
Kinaan Khan Sherwani
0

Ruby 2.5

Erb

Itu mungkin, tetapi Anda harus mendeklarasikan nilai default Anda dalam cakupan.

VARIABEL kata untuk penggantian.

# index.html.erb
...
<%= render 'some_content', VARIABLE: false %>
...

# _some_content.html.erb
...
<% VARIABLE = true if local_assigns[:VARIABLE].nil? %>
<% if VARIABLE %>
    <h1>Do you see me?</h1>
<% end %>
...
dimpiax
sumber
-6

Helper dapat dibuat agar terlihat seperti ini:

somearg = opt(:somearg) { :defaultvalue }

Diimplementasikan seperti:

module OptHelper
  def opt(name, &block)
    was_assigned, value = eval(
      "[ local_assigns.has_key?(:#{name}), local_assigns[:#{name}] ]", 
      block.binding)
    if was_assigned
      value
    else
      yield
    end
  end
end

Lihat blog saya untuk detail tentang bagaimana dan mengapa.

Perhatikan bahwa solusi ini memungkinkan Anda untuk memberikan nil atau salah sebagai nilai tanpa ditimpa.

Jaime Cham
sumber