Rails源码研究之ActionView:五,prototype_helper

本文深入探讨Rails框架中AJAX功能的实现方式及其与Prototype JavaScript库的关系,详细解析了link_to_remote、periodically_call_remote等核心方法,并介绍了JavaScriptGenerator类在RJS中的作用。
Rails的Ajax是依赖于prototype库的,我们来看看ActionView对prototype的一些方法调用的封装
源文件为prototype_helper.rb,其中包括RJS的update_page调用的JavaScriptGenerator类的定义:
1,link_to_remote是简单的异步url请求
[code]
def link_to_remote(name, options = {}, html_options = {})
link_to_function(name, remote_function(options), html_options)
end

def remote_function(options)
javascript_options = options_for_ajax(options)
update = ''
if options[:update] && options[:update].is_a?(Hash)
update = []
update << "success:'#{options[:update][:success]}'" if options[:update][:success]
update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
update = '{' + update.join(',') + '}'
elsif options[:update]
update << "'#{options[:update]}'"
end
function = update.empty? ?
"new Ajax.Request(" :
"new Ajax.Updater(#{update}, "
url_options = options[:url]
url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
function << "'#{url_for(url_options)}'"
function << ", #{javascript_options})"
function = "#{options[:before]}; #{function}" if options[:before]
function = "#{function}; #{options[:after]}" if options[:after]
function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
return function
end
[/code]
link_to_remote调用remote_function组织Prototype方法调用,然后调用link_to_function生成html标签和绑定onclick事件
看看例子:
[code]
<%= link_to_remote "Delete this post", :update => "posts",
:url => { :action => "destroy", :id => post.id } %>

<%= link_to_remote "Undo", :url => { :action => "undo", :n => word_counter },
:complete => "undoRequestCompleted(request)" %>
[/code]
其中:update参数用来在Ajax异步调用完成后更新页面DOM元素,通常controller返回的结果为一个partial

2,periodically_call_remote用来做周期性url请求
[code]
def periodically_call_remote(options = {})
frequency = options[:frequency] || 10
code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})"
javascript_tag(code)
end
[/code]
它new一个PeriodicalExecuter对象来完成周期性的工作,而callbacks等参数和link_to_remote一样,其中:frequency默认是10秒,看看例子:
[code]
<%= periodically_call_remote :update => 'time_div',
:url => {:action => "time"},
:frequency => 1.0 %>
[/code]

3,observe_form和observe_field用来监听form和field,如果内容改变,则触发function调用
[code]
def observe_form(form_id, options = {})
if options[:frequency]
build_observer('Form.Observer', form_id, options)
else
build_observer('Form.EventObserver', form_id, options)
end
end

def observe_field(field_id, options = {})
if options[:frequency] && options[:frequency] > 0
build_observer('Form.Element.Observer', field_id, options)
else
build_observer('Form.Element.EventObserver', field_id, options)
end
end

protected
def build_observer(klass, name, options = {})
if options[:with] && !options[:with].include?("=")
options[:with] = "'#{options[:with]}=' + value"
else
options[:with] ||= 'value' if options[:update]
end
callback = options[:function] || remote_function(options)
javascript = "new #{klass}('#{name}', "
javascript << "#{options[:frequency]}, " if options[:frequency]
javascript << "function(element, value) {"
javascript << "#{callback}}"
javascript << ", '#{options[: on]}'" if options[: on]
javascript << ")"
javascript_tag(javascript)
end
[/code]
build_observer方法给出了生成JavaScript的实现(这段代码看着有点reflection的意思),然后使用javascript_tag来将JavaScript写入模板
看看例子:
[code]
<%= observe_form "entry-form", :frequency => 1,
:update => "live-preview",
:complete => "Element.show('live-preview')",
:url => {:action => "preview"} %>

<%= observe_field 'search', :frequency => 0.5,
:update => 'target_id',
:url => { :controller => '<controller>', :action=> 'list' },
:with => "'search=' + escape(value)" %>
[/code]

4,JavaScriptGenerator
[code]
def update_page(&block)
JavaScriptGenerator.new(@template, &block).to_s
end

class JavaScriptGenerator

def initialize(context, &block) #:nodoc:
@context, @lines = context, []
include_helpers_from_context
@context.instance_exec(self, &block)
end

private
def include_helpers_from_context
@context.extended_by.each do |mod|
extend mod unless mod.name =~ /^ActionView::Helpers/
end
extend GeneratorMethods
end

module GeneratorMethods

def to_s
returning javascript = @lines * $/ do
if ActionView::Base.debug_rjs
source = javascript.dup
javascript.replace "try {\n#{source}\n} catch (e) "
javascript << "{ alert('RJS error:\\n\\n' + e.toString()); alert('#{source.gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }}'); throw e }"
end
end
end

def [](id)
JavaScriptElementProxy.new(self, id)
end

def select(pattern)
JavaScriptElementCollectionProxy.new(self, pattern)
end

def insert_html(position, id, *options_for_render)
insertion = position.to_s.camelize
call "new Insertion.#{insertion}", id, render(*options_for_render)
end

def replace_html(id, *options_for_render)
call 'Element.update', id, render(*options_for_render)
end

def replace(id, *options_for_render)
call 'Element.replace', id, render(*options_for_render)
end

def remove(*ids)
loop_on_multiple_args 'Element.remove', ids
end

def show(*ids)
loop_on_multiple_args 'Element.show', ids
end

def hide(*ids)
loop_on_multiple_args 'Element.hide', ids
end

def toggle(*ids)
loop_on_multiple_args 'Element.toggle', ids
end

def alert(message)
call 'alert', message
end

def redirect_to(location)
assign 'window.location.href', @context.url_for(location)
end

def call(function, *arguments, &block)
record "#{function}(#{arguments_for_call(arguments, block)})"
end

def delay(seconds = 1)
record "setTimeout(function() {\n\n"
yield
record "}, #{(seconds * 1000).to_i})"
end

def visual_effect(name, id = nil, options = {})
record @context.send(:visual_effect, name, id, options)
end

def sortable(id, options = {})
record @context.send(:sortable_element_js, id, options)
end

def draggable(id, options = {})
record @context.send(:draggable_element_js, id, options)
end

def drop_receiving(id, options = {})
record @context.send(:drop_receiving_element_js, id, options)
end

def call(function, *arguments, &block)
record "#{function}(#{arguments_for_call(arguments, block)})"
end

private

def page
self
end

def record(line)
returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do
self << line
end
end

def method_missing(method, *arguments)
JavaScriptProxy.new(self, method.to_s.camelize)
end

end
end
[/code]
我们终于看到了update_page这个helper方法了,它初始化JavaScriptGenerator对象,然后调用to_s方法返回生成的JavaScript代码
而update_page后的block中page方法返回self,即返回JavaScriptGenerator对象
JavaScriptGenerator类中定义了select,replace,hide,delay,visual_effect等我们熟悉的helper方法
而其中visual_effect,sortable,draggable和drop_receiving实际上会调用scriptaculous_helper中相应的方法
看看RJS的例子:
[code]
update_page do |page|
page.insert_html :bottom, 'list', "<li>#{@item.name}</li>"
page.visual_effect :highlight, 'list'
page.hide 'status-indicator', 'cancel-link'
end
[/code]
而[]方法和select方法分别使用JavaScriptElementProxy和JavaScriptElementCollectionProxy代理类

如果大家想了解和熟悉RJS语法,多看看javascript_helper.rb,prototype_helper.rb,scriptaculous_helper.rb
需求响应动态冰蓄冷系统与需求响应策略的优化研究(Matlab代码实现)内容概要:本文围绕“需求响应动态冰蓄冷系统与需求响应策略的优化研究”展开,基于Matlab代码实现,重点探讨了冰蓄冷系统在电力需求响应背景下的动态建模与优化调度策略。研究结合实际电力负荷与电价信号,构建系统能耗模型,利用优化算法对冰蓄冷系统的运行策略进行求解,旨在降低用电成本、平衡电网负荷,并提升能源利用效率。文中还提及该研究为博士论文复现,涉及系统建模、优化算法应用与仿真验证等关键技术环节,配套提供了完整的Matlab代码资源。; 适合人群:具备一定电力系统、能源管理或优化算法基础,从事科研或工程应用的研究生、高校教师及企业研发人员,尤其适合开展需求响应、综合能源系统优化等相关课题研究的人员。; 使用场景及目标:①复现博士论文中的冰蓄冷系统需求响应优化模型;②学习Matlab在能源系统建模与优化中的具体实现方法;③掌握需求响应策略的设计思路与仿真验证流程,服务于科研项目、论文写作或实际工程方案设计。; 阅读建议:建议结合提供的Matlab代码逐模块分析,重点关注系统建模逻辑与优化算法的实现细节,按文档目录顺序系统学习,并尝试调整参数进行仿真对比,以深入理解不同需求响应策略的效果差异。
综合能源系统零碳优化调度研究(Matlab代码实现)内容概要:本文围绕“综合能源系统零碳优化调度研究”,提供了基于Matlab代码实现的完整解决方案,重点探讨了在高比例可再生能源接入背景下,如何通过优化调度实现零碳排放目标。文中涉及多种先进优化算法(如改进遗传算法、粒子群优化、ADMM等)在综合能源系统中的应用,涵盖风光场景生成、储能配置、需求响应、微电网协同调度等多个关键技术环节,并结合具体案例(如压缩空气储能、光热电站、P2G技术等)进行建模与仿真分析,展示了从问题建模、算法设计到结果验证的全流程实现过程。; 适合人群:具备一定电力系统、能源系统或优化理论基础,熟悉Matlab/Simulink编程,从事新能源、智能电网、综合能源系统等相关领域研究研究生、科研人员及工程技术人员。; 使用场景及目标:①开展综合能源系统低碳/零碳调度的科研建模与算法开发;②复现高水平期刊(如SCI/EI)论文中的优化模型与仿真结果;③学习如何将智能优化算法(如遗传算法、灰狼优化、ADMM等)应用于实际能源系统调度问题;④掌握Matlab在能源系统仿真与优化中的典型应用方法。; 阅读建议:建议结合文中提供的Matlab代码与网盘资源,边学习理论模型边动手调试程序,重点关注不同优化算法在调度模型中的实现细节与参数设置,同时可扩展应用于自身研究课题中,提升科研效率与模型精度。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值