Ruby China
  • Topics
  • 招聘
  • Wiki
  • 酷站
  • Gems
  • Sign Up
  • Sign In
Rei
@Rei
Admin
NO. 1 / 2011-10-28

[email protected]
深圳
194 Topics / 9191 Replies
732 Followers
0 Following
11 Favorites
中下水平 Rails 程序员
Reward
GitHub Public Repos
  • writings 941

    [Closed] Source code of writings.io

  • alipay 731

    Unofficial alipay ruby gem

  • code_campo 289

    [Closed] Source code of http://codecampo.com

  • asciidoctor-pdf-cjk-ka... 102

    **no longer maintained**

  • geeknote 38

  • material-ui 17

  • rich-text-editor 12

  • htmlrenderer 12

  • llmrpg 8

  • rails-chatgpt-demo 8

More on GitHub
  • Overview
  • Topics
  • Replies
  • Favorites
  • Following
  • Followers
  • 新人求教:终端不自动载入 RVM 环境 at September 21, 2018

    Docker

  • 新人求教:终端不自动载入 RVM 环境 at September 21, 2018

    要额外处理 PATH 问题。

  • 新人求教:终端不自动载入 RVM 环境 at September 20, 2018
    1. 不建议看 wiki
    2. 不建议用 RVM
    3. 建议装系统源里的 Ruby
  • 想请教一下有关 rails test 无法正确运行的问题 at September 12, 2018

    估计是那几个 gem 导致错误捕捉除了问题:

    group :test do
      gem 'rails-controller-testing', '1.0.2'
      gem 'minitest-reporters', '1.1.14'
      gem 'guard', '2.13.0'
      gem 'guard-minitest', '2.4.4'
    end
    

    我看了下这本书只说了替换 Gemfile 没说更换这些 gem 的理由是什么,对初学者很不利。

    我推荐用《Rails 5 敏捷开发》这本书 https://book.douban.com/subject/27615703/

  • 想请教一下有关 rails test 无法正确运行的问题 at September 12, 2018

    贴代码和运行结果。

  • 想请教一下有关 rails test 无法正确运行的问题 at September 12, 2018

    一个 test case 是指一个 test "..." do end 块,可以随意自己添加。

  • 想请教一下有关 rails test 无法正确运行的问题 at September 12, 2018

    错误打出异常栈是正常的,不然不好 debug,应该是书缺少提示了。

    在同一个 test case 里面,遇到 error 或者 fail 之后,后面的代码就不执行了,因为很可能也会失败,没必要执行下去。test case 之间是独立的。

  • 想请教一下有关 rails test 无法正确运行的问题 at September 12, 2018

    路由未定义。

    errors 打出错误栈是正确的,书里有没有写“以下省略部分输出”?

  • [折腾向] 使用 systemd 来管理你的 Rails 应用 at September 11, 2018

    说 docker 的说说用什么编排工具啊,总不能一台台上去操作吧。

  • [折腾向] 使用 systemd 来管理你的 Rails 应用 at September 11, 2018

    创建目录、链接文件、运行迁移、重启进程,这些工作总要有工具做啊。

    cap 就是个远程执行工具。

  • [折腾向] 使用 systemd 来管理你的 Rails 应用 at September 11, 2018

    我用 cap 部署,systemd 监视进程。

  • Why Sometimes I Write WET Code at September 09, 2018

    写第三次再提炼。

  • 如何本地编译 asset ,然后推送到云端使用? at September 08, 2018

    云端的部署架构是什么?

    如果是典型的 nginx 跟 app 在同一服务器,要把 nginx 的 root 指向 public 目录;如果是 heroku,则需要设置 RAILS_SERVE_STATIC_FILES 环境变量让 rails 进程处理静态文件,但这通常会在前面加一层 CDN。

  • 关于 Rails 写移动端的困惑,希望大佬们能指点一二! at September 06, 2018

    …你先买本入门书把例子做完。

  • 关于 Rails 写移动端的困惑,希望大佬们能指点一二! at September 06, 2018

    API 控制器收到的数据存到模型层,后台控制器再从模型层查数据。

  • 大家是如何处理 Rails 应用的 Model 层和数据库层的数据校验的? at September 05, 2018

    用 validates 比较容易收集错误信息,而且不用访问数据库。
    数据库异常不好处理错误信息,而且耗费了网络请求。

  • ChangeSet 的思路 Rails 会借鉴么? at September 05, 2018

    不承认对象客观存在,又变着法子模仿对象的功能,只能说开心就好。

  • 在 rails 里面 devise 这样的东西 java 或者 python 里面存在吗? at September 05, 2018

    你满嘴跑火车偶尔说对一两句话不出奇,我点赞对事不对人。看我博客发布日期,你确定不是看过我博客后记了下来?

  • 在 rails 里面 devise 这样的东西 java 或者 python 里面存在吗? at September 05, 2018

    可笑 https://chloerei.com/2013/11/22/you-do-not-need-these-gems/

  • ChangeSet 的思路 Rails 会借鉴么? at September 05, 2018

    Ops, 我忘了 elixir 相对 erlang 的改变之一就是增加了可重复绑定的变量,这在 erlang 认为是不纯。

    直接修改实例变量,不比新增一个 struct 然后覆盖它更直观吗?

    order.status = 'paid'
    
  • ChangeSet 的思路 Rails 会借鉴么? at September 05, 2018

    不懂你的观点。

    Struct 用来约束数据结构,数据一旦生成就不可变,要改变的时候只能新增一个数据。例如:

    defmodule Order do
      defstruct number: '', status: 'open'
    end
    
    iex> order = %Order{ number: '20180101' }
    %Order{number: '20180101', status: 'open'}
    iex> order_paid = %{order | status: 'paid'}
    %Order{number: '20180101', status: 'paid'}
    iex> order
    %Order{number: '20180101', status: 'open'} # 旧的数据依然存在
    

    此时内存中存在两份 number 为 20180101 的订单,就因为 elixir 的变量不可变,新变量只是在假装不知道旧数据存在罢了。我认为这不是很好的对现实关系的映射。

  • ChangeSet 的思路 Rails 会借鉴么? at September 05, 2018

    用脚可以拿筷子吗?练练也是可以用的,但是有手的情况下何须用脚拿筷子。

    无状态在处理数据流的时候也许有不少优点,但在跟 DB 交互这里,DB 作为数据流终点就是有状态的,强用无状态实现不是一个优势。

  • ChangeSet 的思路 Rails 会借鉴么? at September 05, 2018

    看了下文档,ChangeSet 不就是 Elixir 这样的函数式语言没有实例变量而不得不把所有变量通过参数传递么。

    而且用起来跟 ActiveRecord 没啥区别:

    changeset = User.changeset(%User{}, %{age: 0, email: "[email protected]"})
    {:error, changeset} = Repo.insert(changeset)
    changeset.errors #=> [age: {"is invalid", []}, name: {"can't be blank", []}]
    
    user = User.new(age: 0, email: "[email protected]")
    user.save
    user.errors #= > <#ActiveModel::Errors ... @message={:age=>["is invalid"], :name=>["cant be blank"]}
    

    看 Ruby 还短一点。

    ChangeSet 用作 validation 一个坏处是很容易被跳过,不能当作数据入库前的守门人。用作 filter 还比较合理。Ruby 2.5 的 yield_self 就可以方便写出链式过滤器调用了。 https://blog.bigbinary.com/2017/12/12/ruby-2-5-added-yield_self.html 下一版也许会把 yield_self 增加一个 alias then。

    学习新语言看到新特性,就感觉无所不能,忘了以前是怎么做的,这是一种通病。函数式语言推广者把“无状态”奉为金科玉律,无视现实世界还有很多有状态的事物,成也于此败也于此。

  • 服务器如何强行指定一次请求头的 Content-Type 为 application/json at September 04, 2018

    不改。对接过 Web、Android、iOS、C++,设请求头都没有问题。说困难是他们忽悠你。

  • 在 rails 里面 devise 这样的东西 java 或者 python 里面存在吗? at September 04, 2018

    举了个例子 https://ruby-china.org/topics/37434#reply-346228

  • 系统使用 Devise 做的登录,有个需求 [同一帐号被另一台机器登录,本机器被强制退出登录],求大神指点一下 at September 04, 2018

    十楼方法是增加一个过滤器判断额外的 token,不改变 devise 内部,原理一样但更安全。

  • 系统使用 Devise 做的登录,有个需求 [同一帐号被另一台机器登录,本机器被强制退出登录],求大神指点一下 at September 04, 2018

    这个问题是个好例子说明为什么 devise 很难用。

    如果是自己实现的用户登录机制,那么很容易会知道应该改哪里。通常来说,user 应该带有一个 token 字段存在 session 中,访问的时候通过 token 查用户。如果要注销旧的登录态,只要把 token 重置就行了。

    那么 devise 怎么做呢,应该 reset 哪个 token?我也不知道,因为内部实现太复杂了,查看数据模式根本没有这个字段,我尝试读一下源码。

    首先根据我以前读源码的经验,我先找 devise 的 session controller:

    https://github.com/plataformatec/devise/blob/3b0bc08ec67dd073ddd6d043c71646c2784ced6c/app/controllers/devise/sessions_controller.rb#L18-L24

    def create
      self.resource = warden.authenticate!(auth_options)
      set_flash_message!(:notice, :signed_in)
      sign_in(resource_name, resource)
      yield resource if block_given?
      respond_with resource, location: after_sign_in_path_for(resource)
    end
    

    关键在于 sign_in(resource_name, resource) 这一行,继续找 sign_in 如何实现:

    https://github.com/plataformatec/devise/blob/715192a7709a4c02127afb067e66230061b82cf2/lib/devise/controllers/sign_in_out.rb#L33-L55

    def sign_in(resource_or_scope, *args)
      options  = args.extract_options!
      scope    = Devise::Mapping.find_scope!(resource_or_scope)
      resource = args.last || resource_or_scope
    
      expire_data_after_sign_in!
    
      if options[:bypass]
        ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc, caller)
        [Devise] bypass option is deprecated and it will be removed in future version of Devise.
        Please use bypass_sign_in method instead.
        Example:
          bypass_sign_in(user)
        DEPRECATION
        warden.session_serializer.store(resource, scope)
      elsif warden.user(scope) == resource && !options.delete(:force)
        # Do nothing. User already signed in and we are not forcing it.
        true
      else
        warden.set_user(resource, options.merge!(scope: scope))
      end
    end
    

    关键在最后一行 warden.set_user(resource, options.merge!(scope: scope)),warden 是另一个 gem,在到另一个库里搜 set_user 做了什么:

    https://github.com/wardencommunity/warden/blob/5b3cbd5bef67cbe399bb7007537bc4841bbee772/lib/warden/proxy.rb

    def set_user(user, opts = {})
      scope = (opts[:scope] ||= @config.default_scope)
    
      # Get the default options from the master configuration for the given scope
      opts = (@config[:scope_defaults][scope] || {}).merge(opts)
      opts[:event] ||= :set_user
      @users[scope] = user
    
      if opts[:store] != false && opts[:event] != :fetch
        options = env[ENV_SESSION_OPTIONS]
        if options
          if options.frozen?
            env[ENV_SESSION_OPTIONS] = options.merge(:renew => true).freeze
          else
            options[:renew] = true
          end
        end
        session_serializer.store(user, scope)
      end
    
      run_callbacks = opts.fetch(:run_callbacks, true)
      manager._run_callbacks(:after_set_user, user, self, opts) if run_callbacks
    
      @users[scope]
    end
    

    关键是这个 session_serializer.store(user, scope),在找找 session_serializer 是什么鬼,然后找到这里:

    https://github.com/wardencommunity/warden/blob/5b3cbd5bef67cbe399bb7007537bc4841bbee772/lib/warden/session_serializer.rb#L23-L28

    def store(user, scope)
      return unless user
      method_name = "#{scope}_serialize"
      specialized = respond_to?(method_name)
      session[key_for(scope)] = specialized ? send(method_name, user) : serialize(user)
    end
    

    我这里跳跃一下,直觉告诉我 warden 里面都是些 proxy 方法,也许 devise 用到的 serialize 是在 deivse 里面定义的,于是我回去搜 devise 里面有没有 serialize 相关的方法,于是搜到这个:

    https://github.com/plataformatec/devise/blob/715192a7709a4c02127afb067e66230061b82cf2/lib/devise/models/authenticatable.rb#L233-L240

    def serialize_into_session(record)
      [record.to_key, record.authenticatable_salt]
    end
    
    def serialize_from_session(key, salt)
      record = to_adapter.get(key)
      record if record && record.authenticatable_salt == salt
    end
    

    我也不知道是不是,看起来 devise 是通过 to_key 和 authenticatable_salt 定位用户的。

    在 ActiveReocrd 里 to_key 约等于 id,不可变,那么我们最好从 authenticatable_salt 入手,搜一下 authenticatable_salt:

    https://github.com/plataformatec/devise/blob/715192a7709a4c02127afb067e66230061b82cf2/lib/devise/models/authenticatable.rb#L98-L99

    def authenticatable_salt
    end
    

    https://github.com/plataformatec/devise/blob/715192a7709a4c02127afb067e66230061b82cf2/lib/devise/models/database_authenticatable.rb#L137-L140

    # A reliable way to expose the salt regardless of the implementation.
    def authenticatable_salt
      encrypted_password[0,29] if encrypted_password
    end
    

    根据加载的模块不同,authenticatable_salt 的实现不一样。留意 authenticatable 里面的实现,authenticatable_salt 跟 encrypted_password 绑定,这样可以实现修改密码之后 authenticatable_salt 一起变更,但这怎么解决顶楼的问题呢?

    我建议是新增一个字段,然后把 authenticatable_salt alias 过去。

    class User < ActiveRecord::Base
      has_secure_token :auth_token
    
      def authenticatable_salt
        auth_token
      end
    end
    

    然后要改写 session_controller,在每次登录之后 reset remember_token:

    def create
      self.resource = warden.authenticate!(auth_options)
      set_flash_message!(:notice, :signed_in)
      resource.regenerate_auth_token # <- 重置 auth_token
      sign_in(resource_name, resource)
      yield resource if block_given?
      respond_with resource, location: after_sign_in_path_for(resource)
    end
    

    以上方法我没有实践过,不保证能用。

    读源码是最费时的一种方法,有的人可能会提醒我,用 Google 搜一下,也许会有现成答案。没错,我搜了一下发现这个特性有人写成了 Gem,https://github.com/phatworx/devise_security_extension (session_limitable)。大多数的 devise 问题都能搜一搜,粘贴段代码,或者装个 gem 解决,但是用户登录是系统的核心模块,这样做真的对吗,如果用户登录的原理都搞不懂,怎么能确保系统是安全的呢?

    所以我的建议是别用 devise,读 devise 源码的耗时远远大于自己写,当然楼主维护现有系统是没得选了。这回复得比较啰嗦,我留作以后讨论 devise 的例子。

  • 部署程序如何能达到一定并发量? at September 03, 2018

    接入 https://newrelic.com/ 再跑压测,看 NewRelic 分析。

  • 如果科学计算生态圈当时使用 Ruby 作为主流而不是 Python,Ruby 能胜任么? at September 02, 2018

    起步吃亏在交流(日语)不通,领域优势确立后就很难追赶。

    绝大部分开发者只会用别人做好的工具,少部分能参与贡献,极少数程序员能创造一个工具甚至一个生态,这极少数人对语言的推广作用是巨大的。远的有 Rails,近的有 Kimurai。

    如果有意为 Ruby 做贡献,尝试从使用者往贡献者进阶,动动手。在 Ruby China 看到好几次抱怨 Ruby 的科学计算工具不完善,但没见一个提过自己有贡献的,这怎么追?

  • Prev
  • 1
  • 2
  • …
  • 33
  • 34
  • 35
  • 36
  • 37
  • …
  • 274
  • 275
  • Next
关于 / RubyConf / Ruby 镜像 / RubyGems 镜像 / 活跃会员 / 组织 / API / 贡献者
由众多爱好者共同维护的 Ruby 中文社区,本站使用 Homeland 构建,并采用 Docker 部署。
服务器由 赞助 CDN 由 赞助
iOS 客户端 / Android 客户端 简体中文 / English