You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Ruby服务对象GrabFeedFromJSE的数据传递至控制器?

实现服务对象数据传递到控制器的方案

我来帮你搞定把GrabFeedFromJSE服务中的数据传递到控制器这件事,先梳理下你的现有代码和调试结果,再一步步给出实现步骤:

服务对象代码(路径:app/services/grab_feed_from_jse.rb)

class GrabFeedFromJSE 
  def initialize(url) 
    require 'oga' 
    require 'net/http' 
    body = Net::HTTP.get(URI.parse(url)) 
    document = Oga.parse_html(body) 
    @titles = document.css('li') 
  end 
end

Pry调试响应

从你用Pry调试的结果来看,@titles已经成功抓取到了页面上的所有li元素,包含了市场指数的名称、成交量、数值、涨跌幅度等关键信息:

[1] pry(#<GrabFeedFromJSE>)> @titles 
=> NodeSet(
  Element(name: "li" children: NodeSet(Text(" "), Element(name: "input" attributes: [Attribute(name: "class" value: "ticker-status"), Attribute(name: "type" value: "hidden"), Attribute(name: "name" value: "Language"), Attribute(name: "value" value: "summary")]))), 
  Element(name: "li" attributes: [Attribute(name: "class" value: "live-ticker-heading")] children: NodeSet(Text("Market Summary for 2018-05-10 "), Element(name: "br"), Text(" "))), 
  Element(name: "li" children: NodeSet(Text("\n\n "), Element(name: "a" attributes: [Attribute(name: "href" value: "/market-data/index-data/jse-index")] children: NodeSet(Text("\n\n \n\n\n JSE Index \n \n "), Element(name: "br"), Text("\n\n 5,201,225 Vol\n "), Element(name: "br"), Text("\n\n 299,198.19 \n "), Element(name: "img" attributes: [Attribute(name: "style" value: "height:16px"), Attribute(name: "src" value: "/img/up.png")]), Text("\n\n \n 22.32\n "))), Text("\n\n\n "))), 
  Element(name: "li" children: NodeSet(Text("\n\n "), Element(name: "a" attributes: [Attribute(name: "href" value: "/market-data/index-data/all-jamaican")] children: NodeSet(Text("\n\n \n\n\n JSE All Jamaican Composite Index \n \n "), Element(name: "br"), Text("\n\n 3,842,904 Vol\n "), Element(name: "br"), Text("\n\n 328,387.59 \n "), Element(name: "img" attributes: [Attribute(name: "style" value: "height:16px"), Attribute(name: "src" value: "/img/up.png")]), Text("\n\n \n 24.50\n "))), Text("\n\n\n "))), 
  Element(name: "li" children: NodeSet(Text("\n\n "), Element(name: "a" attributes: [Attribute(name: "href" value: "/market-data/index-data/jse-select")] children: NodeSet(Text("\n\n \n\n\n JSE Select Index \n \n "), Element(name: "br"), Text("\n\n 1,995,939 Vol\n "), Element(name: "br"), Text("\n\n 8,948.62 \n "), Element(name: "img" attributes: [Attribute(name: "style" value: "height:16px"), Attribute(name: "src" value: "/img/down.png")]), Text("\n\n \n -31.23\n "))), Text("\n\n\n "))), 
  Element(name: "li" children: NodeSet(Text("\n\n "), Element(name: "a" attributes: [Attribute(name: "href" value: "/market-data/index-data/jse-junior")] children: NodeSet(Text("\n\n \n\n\n Junior Market Index \n \n "), Element(name: "br"), Text("\n\n 1,858,849 Vol\n "), Element(name: "br"), Text("\n\n 2,917.69 \n "), Element(name: "img" attributes: [Attribute(name: "style" value: "height:16px"), Attribute(name: "src" value: "/img/down.png")]), Text("\n\n \n -15.45\n "))), Text("\n\n\n "))), 
  Element(name: "li" children: NodeSet(Text("\n\n "), Element(name: "a" attributes: [Attribute(name: "href" value: "/market-data/index-data/combined-index")] children: NodeSet(Text("\n\n \n\n\n JSE Combined Index \n \n "), Element(name: "br"), Text("\n\n 7,028,074 Vol\n "), Element(name: "br"), Text("\n\n 306,927.60 \n "), Element(name: "img" attributes: [Attribute(name: "style" value: "height:16px"), Attribute(name: "src" value: "/img/down.png")]), Text("\n\n \n -148.29\n "))), Text("\n\n\n "))), 
  Element(name: "li" children: NodeSet(Text("\n\n "), Element(name: "a" attributes: [Attribute(name: "href" value: "/market-data/index-data/us-equities")] children: NodeSet(Text("\n\n \n\n\n JSE USD Equities Index \n \n "), Element(name: "br"), Text("\n\n 220,397 Vol\n "), Element(name: "br"), Text("\n\n 165.29 \n "), Element(name: "img" attributes: [Attribute(name: "style" value: "height:16px"), Attribute(name: "src" value: "/img/down.png")]), Text("\n\n \n -8.87\n "))), Text("\n\n\n "))), 
  Element(name: "li" children: NodeSet(Text("\n\n "), Element(name: "a" attributes: [Attribute(name: "href" value: "/market-data/listed-companies/trade-information/CCC/latest")] children: NodeSet(Text("\n\n \n\n\n CCC\n \n "), Element(name: "br"), Text("\n\n 979,272 Vol\n "), Element(name: "br"), Text("\n\n $37.25 \n "), Element(name: "img" attributes: [Attribute(name: "style" value: "height:16px"), Attribute(name: "src" value: "/img/down.png")]), Text("\n\n \n -0.05\n "))), Text("\n\n\n "))), 
  Element(name: "li" children: NodeSet(Text("\n\n "), Element(name: "a" attributes: [Attribute(name: "href" value: "/market-data/listed-companies/trade-information/CAR/latest")] children: NodeSet(Text("\n\n \n\n\n CAR\n \n "), Element(name: "br"), Text("\n\n 109,202 Vol\n "), Element(name: "br"), Text("\n\n $10.60 \n "), Element(name: "img" attributes: [Attribute(name: "style" value: "height:16px"), Attribute(name: 
[2] pry(#<GrabFeedFromJSE>)> ls @titles 
ActiveSupport::ToJsonWithActiveSupportEncoder#methods: to_json 
Enumerable#methods: all? collect_concat each_slice find_index group_by_day_of_month group_by_mon

实现数据传递到控制器的步骤

1. 给服务对象添加公共方法暴露数据

目前你的服务只有初始化方法,@titles是私有实例变量,控制器无法直接访问。我们可以添加一个公共方法,既可以直接返回原始的NodeSet,也可以把数据整理成更易用的哈希数组(推荐后者,这样视图层处理起来更方便)。

修改后的服务代码:

class GrabFeedFromJSE 
  def initialize(url) 
    require 'oga' 
    require 'net/http' 
    body = Net::HTTP.get(URI.parse(url)) 
    document = Oga.parse_html(body) 
    @titles = document.css('li') 
  end

  # 整理并返回结构化的市场数据
  def market_data
    # 过滤掉不需要的li元素(比如标题、隐藏输入框所在的li)
    valid_list_items = @titles.reject do |li| 
      li.attributes['class'] == 'live-ticker-heading' || li.children.any? { |child| child.name == 'input' }
    end

    # 把每个有效li转换成哈希
    valid_list_items.map do |li|
      a_tag = li.at_css('a')
      next unless a_tag # 跳过没有a标签的li

      # 提取a标签中的文本内容,清理掉多余的换行和空格
      text_parts = a_tag.text.strip.split("\n").map(&:strip).reject(&:empty?)
      
      {
        name: text_parts[0],
        volume: text_parts[1].gsub('Vol', '').strip,
        current_value: text_parts[2].strip,
        change_amount: text_parts[3].strip,
        change_direction: a_tag.at_css('img')&.attributes['src']&.value == '/img/up.png' ? :up : :down,
        detail_url: a_tag.attributes['href'].value
      }
    end.compact # 移除nil值
  end

  # 如果需要直接获取原始的NodeSet,添加这个方法
  attr_reader :titles
end

2. 在控制器中调用服务并获取数据

在你的控制器里,实例化服务对象,调用market_data(或者titles)方法获取数据,然后赋值给实例变量,这样视图就能访问到:

class MarketsController < ApplicationController
  def index
    # 替换成实际的JSE市场数据URL
    jse_feed_url = 'https://www.jamse.com/market-summary'
    jse_grabber = GrabFeedFromJSE.new(jse_feed_url)
    
    # 获取结构化的市场数据
    @market_summary = jse_grabber.market_data
    
    # 如果需要原始的NodeSet,用下面这行
    # @raw_titles = jse_grabber.titles
  end
end

3. 在视图中渲染数据

现在你可以在对应的视图文件(比如app/views/markets/index.html.erb)里使用@market_summary来展示数据:

<h1>JSE Market Summary</h1>
<div class="market-cards">
  <% @market_summary.each do |item| %>
    <div class="card">
      <h3><%= item[:name] %></h3>
      <p class="value"><%= item[:current_value] %></p>
      <p class="change <%= item[:change_direction] %>">
        <%= item[:change_amount] %>
        <%= item[:change_direction] == :up ? '↑' : '↓' %>
      </p>
      <p class="volume">Volume: <%= item[:volume] %></p>
      <%= link_to 'View Details', item[:detail_url], class: 'btn' %>
    </div>
  <% end %>
</div>

内容的提问来源于stack exchange,提问作者marcamillion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:27:14