Предположим, что у вас есть следующий код
fruits = ("apples", "bananas", "loganberries")
def eat(food=fruits):
...
Когда я вижу декларацию о еде, наименее удивительной является мысль, что если первый параметр не указан, он будет равен tuple ("apples", "bananas", "loganberries")
Однако, предположим позже в коде, я делаю что-то вроде
def some_random_function():
global fruits
fruits = ("blueberries", "mangos")
, тогда, если параметры по умолчанию были связаны с выполнением функции, а не с объявлением функции, тогда я бы удивляйтесь (очень плохо), чтобы обнаружить, что фрукты были изменены. Это было бы более удивительно ИМО, чем обнаружение того, что ваша функция foo
, указанная выше, мутировала список.
Реальная проблема заключается в изменяемых переменных, и все языки имеют определенную проблему. Возникает вопрос: предположим, что в Java у меня есть следующий код:
StringBuffer s = new StringBuffer("Hello World!");
Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>();
counts.put(s, 5);
s.append("!!!!");
System.out.println( counts.get(s) ); // does this work?
Теперь, использует ли моя карта значение ключа StringBuffer
, когда оно было помещено на карту, или оно хранит ключ по ссылке? В любом случае, кто-то удивлен; либо человек, который попытался получить объект из Map
, используя значение, идентичное тому, с которым он положил его, или человек, который, похоже, не может получить свой объект, даже если ключ, который они используют, буквально тот же объект, который использовался для размещения его на карте (на самом деле Python не позволяет использовать его изменяемые встроенные типы данных в качестве словарных клавиш).
Ваш пример хороший случая, когда новички Python будут удивлены и укушены. Но я бы сказал, что если бы мы «исправили» это, тогда это создало бы другую ситуацию, в которой они были бы укушены, и это было бы еще менее интуитивным. Более того, это всегда имеет место при работе с изменяемыми переменными; вы всегда сталкиваетесь с ситуациями, когда кто-то может интуитивно ожидать одно или наоборот поведения в зависимости от того, какой код они пишут.
Мне лично нравится текущий подход Python: аргументы функции по умолчанию оцениваются, когда функция определена, и что объект всегда является значением по умолчанию. Я предполагаю, что они могут использовать специальный случай с пустым списком, но такой специальный корпус вызовет еще большее удивление, не говоря уже о несовместимости в обратном направлении.
Вы можете редактировать и добавлять переменные в массив params в контроллере, а затем обращаться к ним в response.js.erb. Вот пример с params[:value]
:
def vote
value = params[:type] == "up" ? 1 : -1
params[:value] = value
@public_comment = PublicComment.find(params[:id])
have_voted = @public_comment.evaluators_for(:pub_votes_up) << @public_comment.evaluators_for(:pub_votes_down)
unless have_voted.include?(@current_user) # vote
@public_comment.add_or_update_evaluation(:"pub_votes_#{params[:type]}", value, @current_user)
else # unvote
@public_comment.delete_evaluation(:"pub_votes_#{params[:type]}", @current_user)
params[:value] = 0
end
respond_to do |format|
format.js # vote.js.erb
end
end
И вот пример, сопровождающий response.js.erb
button = $('<%= ".pub#{params[:type]}_#{params[:id]}" %>')
label = button.find('strong')
<% comment = PublicComment.find(params[:id]) %>
label.html('<%= comment.reputation_for(:"pub_votes_#{params[:type]}").to_i %>')
<% if params[:value] == 1 %>
button.addClass('btn-success')
<% elsif params[:value] == -1 %>
button.addClass('btn-danger')
<% else %>
if button.hasClass('btn-success') { button.removeClass('btn-success') }
if button.hasClass('btn-danger') { button.removeClass('btn-danger') }
<% end %>
Вместо использования скрытого поля я решил добавить атрибут данных в контейнер div, который jquery может выбрать.
<div class="searchResults" data-query="<%= @q %>"></div>
, затем jquery для доступа к нему
url: "/search/get_results?search[q]=" + $(".searchResults").data("query") + "&page=" + p
Я считаю, что это самый чистый способ передачи данных на javascript. Не нашел способа передать переменную в файл сценария кофе с конвейером ресурсов рельсов с контроллера. Это метод, который я сейчас использую. Не могу дождаться, пока кто-то установит путь контроллера с рельсами, которые будут лучшими.
несколько способов, которые я сделал в прошлом
, помещают данные в скрытые поля, получают доступ к данным в js / coffee
# single value
<%= hidden_field_tag "foo_name", @foo.name, { :id => "foo-name" } %>
$('#foo-name').val();
# when the 'value' has multiple attributes
<%= hidden_field_tag "foo", @foo.id, { :id => "foo", "data-first-name" => @foo.first_name, "data-last-name" => @foo.last_name } %>
$foo = $('#foo')
console.log $foo.val()
console.log $foo.data("firstName")
console.log $foo.data("lastName")
другой опции: загружать данные в js-структуру данных в erb, получить доступ к ней из js / coffee
<% content_for(:head) do %>
<script>
window.App = window.App || {};
window.App.Data = window.App.Data || {};
window.App.Data.fooList = [
<% @list.each do |foo| %>
<%= foo.to_json %>,
<% end %>
];
</script>
<% end %>
# coffee
for foo in window.App.Data.fooList
console.log "#{foo.id}, #{foo.first_name} #{foo.last_name}"
Я не большой поклонник создания javascript-данных из ruby в erb, как это, что-то об этом просто кажется неправильным - это может быть хотя
и еще один вариант: сделать ajax-вызов и получить данные по запросу с сервера
. Меня также интересуют другие идеи и подходы
В этой конкретной теме есть отличная рельсовая передача и довольно недавняя (февраль 2012): # 324 Передача данных на JavaScript
Это показывает три способа: сценарий тег, атрибут данных и камень Gon. Я думаю, что дом покрыл все доступные методы. Я бы сказал только, что использование вызова AJAX лучше всего использовать, когда у вас большой объем данных, динамические данные или их комбинация.
В контроллере:
@foo_attr = { "data-foo-1" => 1, "data-foo-2" => 2 }
В представлении (HAML):
#foo{@foo_attr}
В ресурсе CoffeeScript:
$("#foo").data("foo-1")
$("#foo").data("foo-2")
В ситуациях, когда ваши данные javascript выходят из-под контроля, использование gon gem по-прежнему является предпочтительным способом перехода на рельсы, даже в 2015 году. После настройки gon вы можете передавать данные в файлы javascript, просто присваивая данные для объекта gon в рельсах.
(Gemfile)
gem 'gon'
(controller)
def index
gon.products = Product.all
(layouts)
<%= include_gon %>
(public/javascripts/your_js_can_be_here.js)
alert(gon.products[0]['id');
(html source automatically produced)
<script>
window.gon = {};
gon.products = [{"created_at":"2015", "updated_at":"2015, "id":1, "etc":"etc"}];
Вы можете прочитать более подробные детали реализации на Gon или два других канала rails-javascript из скринкаста Райана Бате. http://railscasts.com/episodes/324-passing-data-to-javascript