قوالب روبي: كيفية تمرير المتغيرات في ERB المطلقة؟
سؤال
لدي قالب ERB ممطر إلى رمز Ruby:
require 'erb'
DATA = {
:a => "HELLO",
:b => "WORLD",
}
template = ERB.new <<-EOF
current key is: <%= current %>
current value is: <%= DATA[current] %>
EOF
DATA.keys.each do |current|
result = template.result
outputFile = File.new(current.to_s,File::CREAT|File::TRUNC|File::RDWR)
outputFile.write(result)
outputFile.close
end
لا أستطيع تمرير المتغير "الحالي" في القالب.
الخطأ هو:
(erb):1: undefined local variable or method `current' for main:Object (NameError)
كيف يمكنني حل هذه المشكلة؟
المحلول 3
فهمتك!
أقوم بإنشاء فئة ارتباطات
class BindMe
def initialize(key,val)
@key=key
@val=val
end
def get_binding
return binding()
end
end
وتمرير مثيل إلى ERB
dataHash.keys.each do |current|
key = current.to_s
val = dataHash[key]
# here, I pass the bindings instance to ERB
bindMe = BindMe.new(key,val)
result = template.result(bindMe.get_binding)
# unnecessary code goes here
end
يبدو ملف القالب .يرب مثل هذا:
Key: <%= @key %>
نصائح أخرى
لحل بسيط، استخدم افتتح:
require 'erb'
require 'ostruct'
namespace = OpenStruct.new(name: 'Joan', last: 'Maragall')
template = 'Name: <%= name %> <%= last %>'
result = ERB.new(template).result(namespace.instance_eval { binding })
#=> Name: Joan Maragall
الرمز أعلاه بسيط بما فيه الكفاية ولكن لديه (على الأقل) مشكلتين: 1) لأنه يعتمد على OpenStruct
, ، الوصول إلى عوائد متغيرة غير موجودة nil
بينما كنت تريد أن تفضل أنه فشل صاخبة. 2) binding
يسمى داخل كتلة، هذا كل شيء، في إغلاق، لذلك يتضمن جميع المتغيرات المحلية في النطاق (في الواقع، هذه المتغيرات ستظل سمات الهيكل!).
إذن إليك حل آخر، وأكثر سرعة ولكن دون أي من هذه المشاكل:
class Namespace
def initialize(hash)
hash.each do |key, value|
singleton_class.send(:define_method, key) { value }
end
end
def get_binding
binding
end
end
template = 'Name: <%= name %> <%= last %>'
ns = Namespace.new(name: 'Joan', last: 'Maragall')
ERB.new(template).result(ns.get_binding)
#=> Name: Joan Maragall
بالطبع، إذا كنت ستستخدم هذا في كثير من الأحيان، تأكد من إنشاء String#erb
التمديد الذي يسمح لك بكتابة شيء مثل "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2)
.
حل بسيط باستخدام ربط:
b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
في التعليمات البرمجية من السؤال الأصلي، فقط استبدال
result = template.result
مع
result = template.result(binding)
سوف يستخدم ذلك سياق كل كتلة بدلا من سياق المستوى الأعلى.
(فقط استخراج التعليق بواسطة Sciurus كإجابة لأنها أقصر وأكثر صحة.)
require 'erb'
class ERBContext
def initialize(hash)
hash.each_pair do |key, value|
instance_variable_set('@' + key.to_s, value)
end
end
def get_binding
binding
end
end
class String
def erb(assigns={})
ERB.new(self).result(ERBContext.new(assigns).get_binding)
end
end
المرجع: http://stoneship.org/essays/erb-and-the-context-object/
لا أستطيع أن أعطيك إجابة جيدة جدا لماذا يحدث هذا لأنني لست متأكدا 100٪ كيف يعمل ERB، ولكن فقط النظر إلى erb rdocs., ، يقول أنك بحاجة إلى binding
ما هو "كائن ملزم أو بروك يستخدم لتعيين سياق تقييم التعليمات البرمجية".
تحاول رمزك أعلاه مرة أخرى واستبدالها فقط
result = template.result
مع
result = template.result(binding)
جعلها تعمل.
أنا متأكد من / آمل أن يقفز شخص ما هنا وتقديم شرح أكثر تفصيلا لما يحدث. هتافات.
تحرير: للحصول على بعض المعلومات الأخرى Binding
وجعل كل هذا أكثر وضوحا قليلا (على الأقل بالنسبة لي)، تحقق من rdoc ملزمة.
تعديل: هذا هو الحل القذر. يرجى الاطلاع على إجابتي الأخرى.
انها غريبة تماما، ولكن إضافة
current = ""
قبل حلقة "for-every" إصلاح المشكلة.
بارك الله في البرمجة النصية ولغاتهم "ميزات اللغة" ...
هذا المقال يفسر هذا بشكل جيد.
http://www.garetherees.co.ukouric7/01/12/create-a-Template-rendering-class-with-erb/
كما قال آخرون، لتقييم ERB مع بعض المتغيرات، تحتاج إلى ملزمة مناسبة. هناك بعض الحلول التي تعاني من تحديد الفصول والأساليب لكنني أعتقد أن أبسط وأمنح معظم السيطرة والأكثر أمانا هي إنشاء ملزمة نظيفة واستخدامها لتحليل ERB. إليك تأخذ الأمر (روبي 2.2.x):
module B
def self.clean_binding
binding
end
def self.binding_from_hash(**vars)
b = self.clean_binding
vars.each do |k, v|
b.local_variable_set k.to_sym, v
end
return b
end
end
my_nice_binding = B.binding_from_hash(a: 5, **other_opts)
result = ERB.new(template).result(my_nice_binding)
أعتقد مع eval
ودون **
يمكن أن يكون الشيء نفسه يعمل مع روبي قديم من 2.1