首页 > 解决方案 > 在 ActionCable 通道中使用控制器方法

问题描述

我有一个 rails api,我正在尝试为 git gui 创建一个实时流功能。我有一个控制器和一个用于提交更新的通道,我想在通道的订阅操作中使用实时流功能。这可能吗?或者有没有更好的 Rails 方法来做到这一点。我认为正确的方法是ActionCable.server.broadcast('commit_updates', { body: return_data })在我的控制器方法中使用,但我没有在任何地方调用我的控制器方法。我在哪里/如何调用它,以便它在订阅它的客户端上运行?还是我应该将逻辑放在通道内?

这是我的 commit_updates_controller.rb

class CommitUpdatesController < ApplicationController
    include ActionController::Live
    require 'listen'
    require 'json'

    $file_path = "#{Dir.getwd}/.git/objects"

    @@commits = {}
    @@commits_array = []
    @@edges_array = []

    def index
    end 

    def live_stream

        # get folders exlcluding ".","..", "pack","info"
        folders = Dir.entries($file_path).select {|folder| /^[a-z0-9]{2}$/i.match(folder) }

        folders.each do |folder|
            files = Dir.children("#{$file_path}/#{folder}")
            files.each do |file|
                CommitUpdate.find_commit_info(folder, file, @@commits_array)
            end 
        end 
        generate_edges

        ActionCable.server.broadcast('commit_updates', { body: return_data })
        p return_data

        # listens to any changes that happen to the git folder while the program is open
        listener = Listen.to($file_path) do |modified, added, removed|

            #   puts(modified: modified, added: added, removed: removed)
            added.each do |new_file_path|
                split_new_file_path = new_file_path.split("/").reject!{|item| item.empty?}
                folder = split_new_file_path[split_new_file_path.length() - 2]
                file = split_new_file_path[split_new_file_path.length - 1]
                CommitUpdate.find_commit_info(folder, file, @@commits_array)
                add_edge(@@edges_array, CommitUpdate.get_commit_info(folder, file))

                ActionCable.server.broadcast('commit_updates', { body: return_data })
            end 
        end

        listener.start
    
        sleep
    end 

    private

    def generate_edges
        if @@commits_array.length != 0
            @@commits_array.each do |commit|
                add_edge(@@commits_array, commit)
            end 
        end 
    end 

    def add_edge(array, commit)
        if commit[:parents] != []
            commit[:parents].each {|parent| @@edges_array.push({from: parent, to: commit[:sha1]})}
        end 
    end 

    def return_data
        ret = {
            :nodes => @@commits_array,
            :links => @@edges_array
        }
        return ret.to_json
    end 
end

这是我的 commit_updates_channel.rb

    class CommitUpdatesChannel < ApplicationCable::Channel 
        def subscribed 
            stream_from 'commit_updates'
        end 
    end

这是我的 commit_updates.rb 模型

class CommitUpdate < ApplicationRecord

    def self.find_commit_info(folder, file, array)
        file_type = self.check_sha1_type(folder, file)
        if file_type == "commit"
            array.push(
                self.get_commit_info(folder, file)
            )
        end 
    end 


    def self.get_commit_info(folder, file)
        author = ""
        parents = []
        commit_message = ""
        unixtime = ""
        decoded_file = `git cat-file -p #{folder}#{file}`
        file_data_array = decoded_file.split("\n").reject!{|item| item.empty?}
        p file_data_array
        
        file_data_array.each do |item|
            split_item = item.split(" ")
            case  split_item[0]
            when "author"
                author = split_item[1..split_item.length() - 4].join(" ")
            when "parent"
                parents.push(split_item[1])
            when "tree"
                next
            when "blob"
                next
            when "committer"
                unixtime = split_item[split_item.length - 2]
            else 
                commit_message = split_item.join(" ")
            end 
        end 

        commit_info = {
            :sha1 => "#{folder}#{file}",
            :parents => parents,
            :commit_message => commit_message,
            :author => author,
            :unixtime => unixtime,
            :id => "#{folder}#{file}",
            :label => "#{folder}#{file[0..4]}...",
            :font => "18px verdana blue",
        }
        return commit_info
    end 


    private

    def self.check_sha1_type(folder, file)
        return `git cat-file -t #{folder}#{file}`.chomp
    end 

end

标签: rubywebsocketruby-on-rails-6actioncable

解决方案


使用控制器live_stream,需要请求这个方法,例如,编写js程序定期调用它。或者一些正在调用它的 bash 脚本。不好。

另一种解决方案是将live_stream逻辑放入后台作业中,该作业将每 X 分钟检查一次提交更新。如果工作发现变化,它就会广播。

更常见的是将广播放在模型回调after_save中,after_commit. 但它看起来不是你的解决方案。


推荐阅读