对待生命,不妨大胆一点,因为我们终将失去它

分类: TIL


  • 在Rails 8中使用SQLite进行全文搜索

    Rails 8中添加了很多SQLite的功能,而且全都是生产可用的级别,比如非常有用的fts, 也就是full-text search,对于中小型应用相当好用,不用再单独起一个服务。
    但该功能全网还没有任何教程或者信息,包括rails官方文档也暂时没有更新,所以只能查代码和一个该功能的PR
    以下是配置fts的基本步骤,供参考:
    场景:给博客应用添加搜索功能,索引标题(title)和内容(content),其中content是rich text类型

    1. 手动创建迁移文件
      bin/rails g migrate CreateVirtualArticleFts
      内容如下:

      class CreateVirtualArticleFts < ActiveRecord::Migration[8.0]
      def change
      create_virtual_table  :article_fts, :fts5, ["content", "title"]
      end
      end
    2. 创建model文件app/models/article_fts.rb
      class ArticleFts < ApplicationRecord
      self.primary_key = "rowid" # 虚拟表只能使用rowid
      end
    3. 创建app/models/concerns/article/full_text_search.rb文件

      module Article::FullTextSearch
      extend ActiveSupport::Concern
      
      included do
      has_one :article_fts, foreign_key: "rowid"
      end
      
      class_methods do
      def full_text_search(input:, limit:)
        where("article_fts.title LIKE ? OR article_fts.content LIKE ?", "%#{input}%", "%#{input}%")
        .joins(:article_fts)
        .limit(limit)
        .distinct
      end
      end
      
      def find_or_create_article_fts
      return if article_fts
      
      sql = ActiveRecord::Base.sanitize_sql_array(
        [
          "INSERT INTO article_fts (rowid, title, content) VALUES (?, ?, ?)",
          id, 
          title || "", 
          content.to_plain_text
        ]
      )
      ActiveRecord::Base.connection.execute(sql)
      end
      end
    4. app/models/article.rb模型中添加导入和回调函数
      include Article::FullTextSearch
      after_save :find_or_create_article_fts
    5. app/controllers/article_controller.rb使用搜索参数
      @per_page = 10
      @articles = if params[:q].present?
            Article.full_text_search(
              input: params[:q],
              limit: @per_page
            )
    6. 添加搜索框的前端代码
      <%= form_tag root_path, method: :get, class: 'search-form' do %>
        <%= search_field_tag :q, params[:q], placeholder: 'Search...' %>
        <%= submit_tag 'Search' %>
      <% end %>

      最后重新db:migrate后就可用了


  • macOS设置默认shell为Nushell

    使用Nushell有一段时间了,今天总算下定决心将其设置为默认shell ,因此记录下macOS设置默认shell的过程

    ⚠️注意!如果你之前没有使用过Nushell,则非常不建议将其设置为默认shell,请先熟悉并使用一段时间后再做决定!

    1. 安装Nushell:brew install nushell

    2. 设置环境变量:
      首先查看当前已有的环境变量:$env | reject config | transpose key val | each {|r| echo $"$env.($r.key) = '($r.val)'"} | str join (char nl)
      正常情况下,要删除$env.PWD$env.SHELL这两个变量,然后剩余的手动添加到nushell到env.nu配置文件中:
      vim /Users/YourUserName/Library/Application Support/nushell/env.nu

    3. 添加Nushell到/etc/shells中:sudo echo "/opt/homebrew/bin/nu" >> /etc/shells
      如果该命令报错:zsh: permission denied: /etc/shells
      则可以手动修改:sudo vim /etc/shells

    4. 修改默认shell:chsh -s /opt/homebrew/bin/nu
      如果命令报错:chsh: /opt/homebrew/bin/nu: non-standard shell
      则检查第二步是否完成

    5. 验证:echo $SHELL

    6. 注销重新登录即可

    PS:由于nushell的配置文件在Application Support文件夹下,所以我还创建了一个软链接到~/.config/nushell下,方便备份和编辑
    ln -s $nu.env-path ~/.config/nushell/env.nu
    ln -s $nu.config-path ~/.config/nushell/config.nu


  • Firefox隐私设置入门指南

    宗旨:适合新手,简易快速

    使用工具:https://github.com/arkenfox/user.js

    参考资料https://github.com/arkenfox/user.js/wiki/3.4-Apply-&-Update-&-Maintain#-apply

    平台:桌面端

    第一步:下载Firefox

    如果从 Mozilla 网站下载的Firefox,会自动生成一个唯一标识符用于遥测。

    因此建议从 Mozilla FTP 下载没有标识符的安装包,截止2024年10月30日的最新版本是132.0

    第二步:安装Firefox

    第三步:下载配置文件

    共需要下载3个文件:

    1. user.js
    2. prefsCleaner:清理无用的配置(wiki)
      1. Linux/Mac:https://github.com/arkenfox/user.js/blob/master/prefsCleaner.sh
      2. Windows:https://github.com/arkenfox/user.js/blob/master/prefsCleaner.bat
    3. updater:将user-overrides.js中的内容附加到user.js中(wiki)
      1. Linux/Mac:https://github.com/arkenfox/user.js/blob/master/updater.sh
      2. Windows:https://github.com/arkenfox/user.js/blob/master/updater.bat

    第四步:配置Firefox

    • 打开Firefox,输入 about:profiles
    • 打开Profile: default-release的Root Directory文件夹,并进入default-release文件夹,然后将下载的3个文件复制进去
    • 可以自行创建user-overrides.js 添加额外的配置,可参考我使用的
    • 关闭Firefox
    • 双击运行prefsCleaner
    • 双击运行updater

    第五步:验证配置

    输入about:support

    查找user.js

    如果显示如下图,则表示应用成功:

    user.js Preferences

    也可在about:config中搜索相关的配置,查看是否和user.js中配置的一样

    维护和更新

    适用于更新user.js或者user-overrides.js

    • 关闭 Firefox 浏览器
    • 运行updater
    • 运行prefsCleaner

    进阶: 阅读arkenfox/user.js的wiki


  • 批量将Miniflux阅读器的条目设置为已读

    由于follow阅读器今天公测,导致服务器卡爆,内容完全刷新不出来。
    这件事让我再次警觉起来,意识到了掌握自己数据的重要性。
    因此我重新启用了Miniflux,并重新导入了源,但同时也丢失了阅读记录,产生了大量的未阅读条目。
    所以只能从数据库层面,进行筛选,将发布时间大于7天的内容全部设置为已读,步骤如下:
    1.首先要连接到miniflux的pg数据库:docker exec -it <container_name> psql -U miniflux -d miniflux
    2.查看会影响多少条记录:

    SELECT COUNT(*) 
    FROM entries 
    WHERE status = 'unread' 
    AND published_at < (CURRENT_TIMESTAMP - INTERVAL '7 days');

    3.如果数量合理,执行更新:

    UPDATE entries 
    SET status = 'read' 
    WHERE status = 'unread' 
    AND published_at < (CURRENT_TIMESTAMP - INTERVAL '7 days');

    4.验证更新结果:

    SELECT COUNT(*) 
    FROM entries 
    WHERE status = 'unread' 
    AND published_at < (CURRENT_TIMESTAMP - INTERVAL '7 days');

    这次应该返回0条记录。


  • Rails的helper_method和Concern

    今天在做用户认证时,在控制器中一直用authenticated?来判断用户是否登陆,但一直不成功。

    查了下,发现authenticated?Authentication Concern的helper_method方法,
    而helper_method是暴露给view层使用的,在其它控制器中是不能直接使用的,
    需要在控制器的class下include Authentication才可以使用


    Conern是Rails非常有用的一个概念,在以前,我经常会写多重继承的代码,
    比如“大象”类继承“哺乳动物”类,而“哺乳动物”类又继承自“动物园”类,
    这时候为了兼容,很容易出现“动物园”类里有很多“大象”类和“哺乳动物”类所不需要的方法,
    这就造成代码重用性的问题,而且也不容易维护。
    而Conern允许将共享的代码逻辑封装到单独的可重用的模块中,通常存放在 app/models/concernsapp/controllers/concerns 目录下,
    然后在需要使用这个 Concern 的类中,使用 include关键字导入就行了

    class SomeController < ApplicationController
      include Authentication
    
      # 现在可以使用 Authentication 中定义的方法和回调
    end

    比如“哺乳动物”类就可以作为一个Conern,“大象”类直接继承自“动物园”类,但可以在“大象”类里include “哺乳动物”类来使用需要的方法。

    有点像 接口/协议 的概念,但接口/协议主要是定义/签名,不一定有方法的实现,而Conern直接提供方法的实现,更注重代码的重用性,其它不用考虑


  • Rails 8的Authentication模块

    如果你像我一样刚开始学习Rails,则建议直接开始使用Rails 8.0.0.beta版本,因为该版本更新优化了很多功能,最重要的是加入了身份验证模块抽象层,不用像7之前的版本那样麻烦。
    在这里记录下我的相关笔记

    • 一键生成身份验证系统: bin/rails generate authentication
      该命令会创建User、Current、Session三个模型,并且会在app/controllers/conerns下添加Authentication模块
    • 只有认证模块,没有注册模块
    • 默认使用邮箱和密码进行认证
    • Authentication模块的方法,建议直接打开该模块文件,就55行代码,非常的简洁明了
    • 查看三个模型对应的的controller文件,也非常简洁明了。。。
    • 认证流程(via):
      file
    • 重置密码流程(via):
      file
    • 添加注册流程

      1. 创建Users Controller: bin/rails g controller Users

        # File: app/controllers/users_controller.rb
        class UsersController < ApplicationController
        allow_unauthenticated_access only: %i[ new create ]
        def new
        end
        
        def create
        @user = User.new(user_params)
        if @user.save
          redirect_to root_path, notice: "User was successfully created."
        else
          render :new
        end
        end
        
        private
        def user_params
        params.require(:user).permit(:email_address, :password, :password_confirmation)
        end
        end
      2. 创建view文件
        #File: app/views/users/new.html.erb
        <%= tag.div(flash[:alert], style: "color:red") if flash[:alert] %>
        <%= tag.div(flash[:notice], style: "color:green") if flash[:notice] %>
        

        Sign Up

        <%= form_with(model:User) do |form| %> <%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address] %>
        <%= form.password_field :password, required: true, placeholder: "Enter your password", maxlength: 72 %>
        <%= form.password_field :password_confirmation, required: true, placeholder: "Confirm your password", maxlength: 72 %>
        <%= form.submit "Sign up" %> <% end %>
      3. 添加route
        #File: config/routes.rb
        resources :users

  • params.fetch和params.require的区别

    这两种方式用于在 Rails 控制器中处理参数,但它们的用法和目的有所不同:

    params.fetch(:article, {})

    • 功能:fetch 方法尝试从 params 中获取 :article 的键值对。如果 :article 存在,它返回对应的值;如果不存在,则返回一个空的哈希 {},而不会抛出异常。
    • 用途:这种方式通常用于你不确定参数是否存在的情况,并且你希望在参数缺失时提供一个默认值。

    params.require(:articles).permit({})

    • 功能:require 方法确保 :articles 参数存在于 params 中。如果不存在,Rails 将抛出 ActionController::ParameterMissing 异常。permit({}) 方法用于指定哪些属性是允许的(在这个例子中,没有任何属性被允许)。
    • 用途:这种方式用于强制性地确保某些必需的参数存在,并用于强参数(Strong Parameters)以指定允许的属性,从而防止批量赋值漏洞。

    总结

    • fetch 用于在找不到参数时提供默认值,而不会引发错误。
    • require 是用于确保参数存在,并与 permit 结合使用以过滤允许的参数。

    在实际开发中,如果你需要控制和验证输入参数的存在和安全性,通常使用 require 和 permit 组合。而 fetch 常用于需要灵活处理参数的情况。


  • docker文件清理

    一键清理Docker无用的文件,释放磁盘空间:docker system prune -a
    删除所有未使用的本地卷: docker volume prune -a


  • Time Machine服务内网分享小记

    当使用Mac mini在局域网内分享Time Machine服务时,一定要关闭系统的防火墙,即使系统会自动添加分享服务到规则里,但客户端依旧会添加失败或者stuck在连接状态。

    如果客户端添加后,一直无法完成初始备份,可以尝试在服务端(mac mini)的“安全和隐私”里,全盘访问权限勾选smbd服务,然后重启即可

    系统版本:macOS 14.6


  • install命令

    在shell脚本中,如果要安装一个应用或者添加配置,一般是这样的:

    mkdir -p ~/.config/app
    cp conf ~/.config/app/conf
    chmod 755 ~/.config/app/conf

    实际上,这些命令可以用install命令就能完成:
    install -D -m 755 conf ~/.config/app/conf

    如果需要设置权限,可以使用-o-g参数
    install -D -m 644 -o root -g root seed.db /var/lib/app/seed.db