هل يمكنك تمرير النفس إلى Lambda في القضبان؟
-
12-09-2019 - |
سؤال
أريد تحديد طريقة الفصل التي لديها حق الوصول إلى متغير محلي. لذلك سيكون هذا مختلفا عن كل حالة من الفصل. أعلم أنه يمكنك جعل طريقة الفصل الديناميكية مع Lambda مثل عند استخدامه مع Named_scope. ولكن هل يمكن القيام بذلك للقيم المحددة بمثيل؟
بالتفصيل، إنها طريقة HAS_ATTACHED_FILE للمكون الإضافي PaperClip في القضبان. أرغب في اجتياز Lambda لصالح الأنماط بحيث يمكن أن تعتمد أنماط الصور من سمات الكائن المخزن في DB. هل هذا ممكن؟
المحلول
تنصل: أولا، السؤال (يمكنك تمرير النفس إلى لامبادا؟) والمشكلة التي تحاول حلها (أنماط ديناميكية مع PaperClip) لا تتطابق تماما. لن أجيب على السؤال الأصلي لأنه غير مرتبط تماما بمشكلتك، واتخذت Rampion طعنة بساطة في ذلك.
سأجيب بدلا من ذلك عن سؤالك الوربي.
بالتفصيل هو
has_attached_file
طريقة للحصول على البرنامج المساعد PaperClip في القضبان. أرغب في اجتياز Lambda لصالح الأنماط بحيث يمكن أن تعتمد أنماط الصور من سمات الكائن المخزن في DB. هل هذا ممكن؟
نعم هذا ممكن. في paperclip، :styles
الخيار يمكن أن تأخذ proc. عند تهيئة المرفق، إذا تم استخدام بروك، يتم تمرير المرفق نفسه إلى Proc. يحتوي المرفق على إشارة إلى كائن ActiverCord المقترن، حتى تتمكن من استخدام ذلك لتحديد أنماط ديناميكية.
على سبيل المثال، الخاص بك has_attached_file
قد يبدو الإعلان شيئا كهذا (على افتراض وجود سيناريو مستخدم وسيناريو باللغة الرمزية حيث يمكن للمستخدم تخصيص حجم الصورة الرمزية الخاصة بهم):
class User < ActiveRecord::Base
has_attached_file :avatar, :styles => lambda { |attachment|
user = attachment.instance
dimensions = "#{user.avatar_width}x#{user.avatar_height}#"
{ :custom => dimensions }
}
end
نصائح أخرى
حسنا، أنت غير واضح.
المتغيرات المحلية في روبي تبدأ بحرف صغير (مثل foo
, bar
, ، أو steve
)، ومجموعة معجمية (مثل C
المتغيرات). ليس لديهم علاقة "بمثيل فئة"
المتغيرات المثلية في روبي تبدأ ب @
سيجيل (مثل @foo
, @bar
, ، أو @carl
)، وهي في نطاق كلما كانت القيمة الحالية لل self
هو الكائن الذي يتم تخزينه فيه.
إذا كنت تريد طريقة يمكنها الوصول إلى متغيرات مثيل كائن مباشرة، فهذا يسمى طريقة مثيل. علي سبيل المثال، battle_cry
و initialize
كلاهما طرق مثيل:
class Character
def initialize(name)
@name=name
end
def battle_cry
@name.upcase + "!!!"
end
def Character.default
new("Leeroy Jenkins")
end
end
طريقة الفصل، على النقيض من ذلك، هي طريقة ل Class
كائن، وليس لديه حق الوصول إلى أي من متغيرات المثيل لهذا الكائن. في المثال أعلاه،default
هي طريقة الفصل.
إذا كنت تريد طريقة (فئة أو مثيل) تقوم بتشغيل تغيير أو يحصل على قيمة من النطاق الحالي، فإن Ruby يستخدم نوعا من رد الاتصال يسمى كتلة.
class Character
ATTACKS = [ "Ho!", "Haha!", "Guard!", "Turn!", "Parry!", "Dodge!", "Spin!", "Ha", "THRUST!" ]
def attack
ATTACKS.inject(0) { |dmg, word| dmg + yield(word) }
end
end
person = Character.default
puts person.battle_cry
num_attacks = 0;
damage = person.attack do |saying|
puts saying
num_attacks += 1
rand(3)
end
puts "#{damage} points of damage done in #{num_attacks} attacks"
في المثال أعلاه، attack
يستخدم yield
الكلمة الرئيسية للاتصال كتلة مرت إليها. عندما ندعو attack
, ، إذن، المتغير المحلي num_attacks
لا يزال النطاق في الكتلة التي نرزها (محددة هنا من قبل do ... end
)، لذلك يمكننا زيادة ذلك. attack
قادر على تمرير القيم في الكتلة، هنا يتم التقاطها في saying
عامل. تقوم الكتلة أيضا بتمرير القيم مرة أخرى إلى الطريقة، والتي تظهر كقيمة إرجاع yield
.
الكلمة lambda
في روبي عادة ما يعني lambda
الكلمة الرئيسية، التي يتم استخدامها لصنع كتل في Freestanding، وظيفة مثل الكائنات (والتي تشار إليها عادة باسم lambda
س، proc
S، أو Proc
س).
bounce = lambda { |thing| puts "I'm bouncing a #{thing}" }
bounce["ball"]
bounce["frog"]
لذلك أعتقد أن ما تسأل هو ما إذا كان يمكنك اجتياز Proc
في مكان Hash
لحجة إلى طريقة. والإجابة هي "ذلك يعتمد". إذا كانت الطريقة تستخدم فقط من أي وقت مضى #[]
الطريقة، ثم نعم:
class Character
attr_accessor :stats
def set_stats(stats)
@stats = stats
end
end
frank = Character.new("Victor Frankenstein")
frank.set_stats({ :str => 7, :dex => 14, :con => 9, :int => 19, :wis => 7, :cha => 11 })
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
ومع ذلك، قد يستخدم البعض الآخر Hash
أساليب محددة، أو استدعاء نفس المفتاح عدة مرات، والتي يمكن أن تنتج نتائج غريبة:
monster = Character.new("Frankenstein's Monster")
monster.set_stats(lambda do |stat_name|
rand(20)
end)
monster.stats[:dex] #=> 19
monster.stats[:dex] #=> 1
في هذه الحالة، قد تكون أفضل حالا في التخزين المؤقت للطلبات في التجزئة الوسيطة. هذا سهل إلى حد ما، منذ Hash
يمكن أن يكون كتلة تهيئة. لذلك إذا قمنا بتغيير ما سبق إلى:
monster.set_stats(Hash.new do |stats_hash, stat_name|
stats_hash[stat_name] = rand(20)
end)
monster.stats[:dex] #=> 3
monster.stats[:dex] #=> 3
يتم تخزين النتائج مؤقتا في التجزئة
لرؤية المزيد عن Hash
كتلة التهيئة، انظر ri Hash::new
:
-------------------------------------------------------------- Hash::new
Hash.new => hash
Hash.new(obj) => aHash
Hash.new {|hash, key| block } => aHash
------------------------------------------------------------------------
Returns a new, empty hash. If this hash is subsequently accessed
by a key that doesn't correspond to a hash entry, the value
returned depends on the style of new used to create the hash. In
the first form, the access returns nil. If obj is specified, this
single object will be used for all default values. If a block is
specified, it will be called with the hash object and the key, and
should return the default value. It is the block's responsibility
to store the value in the hash if required.
h = Hash.new("Go Fish")
h["a"] = 100
h["b"] = 200
h["a"] #=> 100
h["c"] #=> "Go Fish"
# The following alters the single default object
h["c"].upcase! #=> "GO FISH"
h["d"] #=> "GO FISH"
h.keys #=> ["a", "b"]
# While this creates a new default object each time
h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
h["c"] #=> "Go Fish: c"
h["c"].upcase! #=> "GO FISH: C"
h["d"] #=> "Go Fish: d"
h.keys #=> ["c", "d"]