开拓者合同优先于params

时间:2017-12-30 23:38:05

标签: ruby-on-rails ruby trailblazer

我有这个操作:

require 'bcrypt'

class User::Create < Trailblazer::Operation

  extend Contract::DSL

  contract 'params', (Dry::Validation.Schema do
    required(:email).filled
    required(:password).filled
  end)

  step      Contract::Validate(name: 'params'), before: 'operation.new'
  step      Model(User, :new)
  success   :generate_token
  success   :encrypt_password
  success   :assign_user_values
  step      Contract::Build(constant: User::Contract::Create)
  step      Contract::Validate()
  step      Contract::Persist()

  def generate_token(options, *)
    token = loop do
      random_token = SecureRandom.uuid
      break random_token unless User.exists?(token: random_token)
    end

    options['data.token'] = token
  end

  def encrypt_password(options, params:, **)
    options['data.password'] = BCrypt::Password.create(params[:password])
  end

  def assign_user_values(options, params:, **)
    options['model'].token = options['data.token']
    options['model'].password = options['data.password']
    options['model'].role = 'user'
  end

end

当我运行操作时,我得到了这个输出:

User::Create.(email: 'bidule@gmail.com', password: 'bidule')
  User Exists (0.6ms)  SELECT  1 AS one FROM "users" WHERE "users"."token" = $1 LIMIT $2  [["token", "64423af4-5741-46af-9756-15715a632d75"], ["LIMIT", 1]]
   (0.6ms)  SELECT COUNT(*) FROM "users" WHERE "users"."email" = $1  [["email", "bidule@gmail.com"]]
   (0.4ms)  SELECT COUNT(*) FROM "users" WHERE "users"."token" = $1  [["token", "64423af4-5741-46af-9756-15715a632d75"]]
   (0.3ms)  BEGIN
  SQL (0.5ms)  INSERT INTO "users" ("email", "password", "role", "token", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"  [["email", "bidule@gmail.com"], ["password", "bidule"], ["role", "user"], ["token", "64423af4-5741-46af-9756-15715a632d75"], ["created_at", "2017-12-30 23:34:24.456817"], ["updated_at", "2017-12-30 23:34:24.456817"]]
   (2.8ms)  COMMIT
=> <Result:true <Skill {"contract.params.params"=>{:email=>"bidule@gmail.com", :password=>"bidule"}, "representer.params.class"=>false, "result.contract.params"=>#<Dry::Validation::Result output={:email=>"bidule@gmail.com", :password=>"bidule"} errors={}>, "model.class"=>User(id: integer, email: string, password: string, role: string, token: string, created_at: datetime, updated_at: datetime), "model.action"=>:new, "model"=>#<User id: 17, email: "bidule@gmail.com", password: "bidule", role: "user", token: "64423af4-5741-46af-9756-15715a632d75", created_at: "2017-12-30 23:34:24", updated_at: "2017-12-30 23:34:24">, "result.model"=><Result:true {} >, "data.token"=>"64423af4-5741-46af-9756-15715a632d75", "data.password"=>"$2a$10$gy19txopkeM5Vovx4/ohvOuZAP2CMwtBTcCwswJq9Ije7NVrKj8Ce", "contract.default"=>#<User::Contract::Create:0x0000000004e316e8 @fields={"email"=>"bidule@gmail.com", "password"=>"bidule", "role"=>"user", "token"=>"64423af4-5741-46af-9756-15715a632d75"}, @model=#<User id: 17, email: "bidule@gmail.com", password: "bidule", role: "user", token: "64423af4-5741-46af-9756-15715a632d75", created_at: "2017-12-30 23:34:24", updated_at: "2017-12-30 23:34:24">, @mapper=#<#<Class:0x0000000004e31558>:0x0000000004e30bf8 @model=#<User id: 17, email: "bidule@gmail.com", password: "bidule", role: "user", token: "64423af4-5741-46af-9756-15715a632d75", created_at: "2017-12-30 23:34:24", updated_at: "2017-12-30 23:34:24">>, @_changes={"email"=>true, "password"=>true}, @errors=#<Reform::Form::ActiveModel::Errors:0x0000000004e30338 @base=#<User::Contract::Create:0x0000000004e316e8 ...>, @messages={}, @details={}>>, "contract.default.params"=>{:email=>"bidule@gmail.com", :password=>"bidule"}, "representer.default.class"=>false, "result.contract.default"=>#<User::Contract::Create:0x0000000004e316e8 @fields={"email"=>"bidule@gmail.com", "password"=>"bidule", "role"=>"user", "token"=>"64423af4-5741-46af-9756-15715a632d75"}, @model=#<User id: 17, email: "bidule@gmail.com", password: "bidule", role: "user", token: "64423af4-5741-46af-9756-15715a632d75", created_at: "2017-12-30 23:34:24", updated_at: "2017-12-30 23:34:24">, @mapper=#<#<Class:0x0000000004e31558>:0x0000000004e30bf8 @model=#<User id: 17, email: "bidule@gmail.com", password: "bidule", role: "user", token: "64423af4-5741-46af-9756-15715a632d75", created_at: "2017-12-30 23:34:24", updated_at: "2017-12-30 23:34:24">>, @_changes={"email"=>true, "password"=>true}, @errors=#<Reform::Form::ActiveModel::Errors:0x0000000004e30338 @base=#<User::Contract::Create:0x0000000004e316e8 ...>, @messages={}, @details={}>>} {"params"=>{:email=>"bidule@gmail.com", :password=>"bidule"}} {"pipetree"=>[>contract.params.validate,>operation.new,>model.build,>generate_token,>encrypt_password,>assign_user_values,>contract.build,>contract.default.validate,>persist.save], "contract.params"=>#<#<Class:0x0000000005261790>:0x0000000005245798 @type_map={}, @config=#<#<Class:0x0000000004b1a518>:0x00000000052616a0 @config={:input_processor=>:noop, :hash_type=>:weak, :type_map=>{}, :path=>[], :predicates=>Dry::Logic::Predicates, :registry=>#<Dry::Validation::PredicateRegistry::Unbound:0x0000000005261588 @external=Dry::Logic::Predicates, @predicates={}>, :messages=>:yaml, :messages_file=>nil, :namespace=>nil, :rules=>[[:rule, [:email, [:and, [[:rule, [:email, [:predicate, [:key?, [[:name, :email], [:input, Undefined]]]]]], [:rule, [:email, [:key, [:email, [:predicate, [:filled?, [[:input, Undefined]]]]]]]]]]]], [:rule, [:password, [:and, [[:rule, [:password, [:predicate, [:key?, [[:name, :password], [:input, Undefined]]]]]], [:rule, [:password, [:key, [:password, [:predicate, [:filled?, [[:input, Undefined]]]]]]]]]]]]], :checks=>[], :options=>{}, :input=>nil, :input_rule=>nil, :dsl_extensions=>nil, :input_processor_map=>{:sanitizer=>#<Dry::Validation::InputProcessorCompiler::Sanitizer:0x0000000004afb618 @type_compiler=#<Dry::Types::Compiler:0x0000000004afb5f0 @registry=Dry::Types>>, :json=>#<Dry::Validation::InputProcessorCompiler::JSON:0x0000000004afb5c8 @type_compiler=#<Dry::Types::Compiler:0x0000000004afb5a0 @registry=Dry::Types>>, :form=>#<Dry::Validation::InputProcessorCompiler::Form:0x0000000004afb578 @type_compiler=#<Dry::Types::Compiler:0x0000000004afb550 @registry=Dry::Types>>}, :type_specs=>false}>, @predicates=#<Dry::Validation::PredicateRegistry::Bound:0x0000000005245720 @external=Dry::Logic::Predicates, @predicates={}, @schema=#<#<Class:0x0000000005261790>:0x0000000005245798 ...>>, @rule_compiler=#<Dry::Validation::SchemaCompiler:0x0000000005245630 @predicates=#<Dry::Validation::PredicateRegistry::Bound:0x0000000005245720 @external=Dry::Logic::Predicates, @predicates={}, @schema=#<#<Class:0x0000000005261790>:0x0000000005245798 ...>>, @options={:predicate_registry=>#<Dry::Validation::PredicateRegistry::Unbound:0x0000000005261588 @external=Dry::Logic::Predicates, @predicates={}>, :message_compiler=>#<Dry::Validation::MessageCompiler:0x00000000052459c8 @messages=#<Dry::Validation::Messages::YAML data={"en.errors.or"=>"or", "en.errors.array?"=>"must be an array", "en.errors.empty?"=>"must be empty", "en.errors.excludes?"=>"must not include %{value}", "en.errors.excluded_from?.arg.default"=>"must not be one of: %{list}", "en.errors.excluded_from?.arg.range"=>"must not be one of: %{list_left} - %{list_right}", "en.errors.exclusion?"=>"must not be one of: %{list}", "en.errors.eql?"=>"must be equal to %{left}", "en.errors.not_eql?"=>"must not be equal to %{left}", "en.errors.filled?"=>"must be filled", "en.errors.format?"=>"is in invalid format", "en.errors.number?"=>"must be a number", "en.errors.odd?"=>"must be odd", "en.errors.even?"=>"must be even", "en.errors.gt?"=>"must be greater than %{num}", "en.errors.gteq?"=>"must be greater than or equal to %{num}", "en.errors.hash?"=>"must be a hash", "en.errors.included_in?.arg.default"=>"must be one of: %{list}", "en.errors.included_in?.arg.range"=>"must be one of: %{list_left} - %{list_right}", "en.errors.inclusion?"=>"must be one of: %{list}", "en.errors.includes?"=>"must include %{value}", "en.errors.bool?"=>"must be boolean", "en.errors.true?"=>"must be true", "en.errors.false?"=>"must be false", "en.errors.int?"=>"must be an integer", "en.errors.float?"=>"must be a float", "en.errors.decimal?"=>"must be a decimal", "en.errors.date?"=>"must be a date", "en.errors.date_time?"=>"must be a date time", "en.errors.time?"=>"must be a time", "en.errors.key?"=>"is missing", "en.errors.attr?"=>"is missing", "en.errors.lt?"=>"must be less than %{num}", "en.errors.lteq?"=>"must be less than or equal to %{num}", "en.errors.max_size?"=>"size cannot be greater than %{num}", "en.errors.min_size?"=>"size cannot be less than %{num}", "en.errors.none?"=>"cannot be defined", "en.errors.str?"=>"must be a string", "en.errors.type?"=>"must be %{type}", "en.errors.size?.arg.default"=>"size must be %{size}", "en.errors.size?.arg.range"=>"size must be within %{size_left} - %{size_right}", "en.errors.size?.value.string.arg.default"=>"length must be %{size}", "en.errors.size?.value.string.arg.range"=>"length must be within %{size_left} - %{size_right}"}>, @options={}, @full=false, @hints=true, @locale=:en, @default_lookup_options={:locale=>:en}>, :input_processor=>#<Proc:0x000000000385d1a0@/app/vendor/bundle/ruby/2.3.0/gems/dry-validation-0.11.1/lib/dry/validation/schema/class_interface.rb:11 (lambda)>, :checks=>[]}, @schema=#<#<Class:0x0000000005261790>:0x0000000005245798 ...>>, @message_compiler=#<Dry::Validation::MessageCompiler:0x00000000052459c8 @messages=#<Dry::Validation::Messages::YAML data={"en.errors.or"=>"or", "en.errors.array?"=>"must be an array", "en.errors.empty?"=>"must be empty", "en.errors.excludes?"=>"must not include %{value}", "en.errors.excluded_from?.arg.default"=>"must not be one of: %{list}", "en.errors.excluded_from?.arg.range"=>"must not be one of: %{list_left} - %{list_right}", "en.errors.exclusion?"=>"must not be one of: %{list}", "en.errors.eql?"=>"must be equal to %{left}", "en.errors.not_eql?"=>"must not be equal to %{left}", "en.errors.filled?"=>"must be filled", "en.errors.format?"=>"is in invalid format", "en.errors.number?"=>"must be a number", "en.errors.odd?"=>"must be odd", "en.errors.even?"=>"must be even", "en.errors.gt?"=>"must be greater than %{num}", "en.errors.gteq?"=>"must be greater than or equal to %{num}", "en.errors.hash?"=>"must be a hash", "en.errors.included_in?.arg.default"=>"must be one of: %{list}", "en.errors.included_in?.arg.range"=>"must be one of: %{list_left} - %{list_right}", "en.errors.inclusion?"=>"must be one of: %{list}", "en.errors.includes?"=>"must include %{value}", "en.errors.bool?"=>"must be boolean", "en.errors.true?"=>"must be true", "en.errors.false?"=>"must be false", "en.errors.int?"=>"must be an integer", "en.errors.float?"=>"must be a float", "en.errors.decimal?"=>"must be a decimal", "en.errors.date?"=>"must be a date", "en.errors.date_time?"=>"must be a date time", "en.errors.time?"=>"must be a time", "en.errors.key?"=>"is missing", "en.errors.attr?"=>"is missing", "en.errors.lt?"=>"must be less than %{num}", "en.errors.lteq?"=>"must be less than or equal to %{num}", "en.errors.max_size?"=>"size cannot be greater than %{num}", "en.errors.min_size?"=>"size cannot be less than %{num}", "en.errors.none?"=>"cannot be defined", "en.errors.str?"=>"must be a string", "en.errors.type?"=>"must be %{type}", "en.errors.size?.arg.default"=>"size must be %{size}", "en.errors.size?.arg.range"=>"size must be within %{size_left} - %{size_right}", "en.errors.size?.value.string.arg.default"=>"length must be %{size}", "en.errors.size?.value.string.arg.range"=>"length must be within %{size_left} - %{size_right}"}>, @options={}, @full=false, @hints=true, @locale=:en, @default_lookup_options={:locale=>:en}>, @input_processor=#<Proc:0x000000000385d1a0@/app/vendor/bundle/ruby/2.3.0/gems/dry-validation-0.11.1/lib/dry/validation/schema/class_interface.rb:11 (lambda)>, @options={:predicate_registry=>#<Dry::Validation::PredicateRegistry::Unbound:0x0000000005261588 @external=Dry::Logic::Predicates, @predicates={}>, :message_compiler=>#<Dry::Validation::MessageCompiler:0x00000000052459c8 @messages=#<Dry::Validation::Messages::YAML data={"en.errors.or"=>"or", "en.errors.array?"=>"must be an array", "en.errors.empty?"=>"must be empty", "en.errors.excludes?"=>"must not include %{value}", "en.errors.excluded_from?.arg.default"=>"must not be one of: %{list}", "en.errors.excluded_from?.arg.range"=>"must not be one of: %{list_left} - %{list_right}", "en.errors.exclusion?"=>"must not be one of: %{list}", "en.errors.eql?"=>"must be equal to %{left}", "en.errors.not_eql?"=>"must not be equal to %{left}", "en.errors.filled?"=>"must be filled", "en.errors.format?"=>"is in invalid format", "en.errors.number?"=>"must be a number", "en.errors.odd?"=>"must be odd", "en.errors.even?"=>"must be even", "en.errors.gt?"=>"must be greater than %{num}", "en.errors.gteq?"=>"must be greater than or equal to %{num}", "en.errors.hash?"=>"must be a hash", "en.errors.included_in?.arg.default"=>"must be one of: %{list}", "en.errors.included_in?.arg.range"=>"must be one of: %{list_left} - %{list_right}", "en.errors.inclusion?"=>"must be one of: %{list}", "en.errors.includes?"=>"must include %{value}", "en.errors.bool?"=>"must be boolean", "en.errors.true?"=>"must be true", "en.errors.false?"=>"must be false", "en.errors.int?"=>"must be an integer", "en.errors.float?"=>"must be a float", "en.errors.decimal?"=>"must be a decimal", "en.errors.date?"=>"must be a date", "en.errors.date_time?"=>"must be a date time", "en.errors.time?"=>"must be a time", "en.errors.key?"=>"is missing", "en.errors.attr?"=>"is missing", "en.errors.lt?"=>"must be less than %{num}", "en.errors.lteq?"=>"must be less than or equal to %{num}", "en.errors.max_size?"=>"size cannot be greater than %{num}", "en.errors.min_size?"=>"size cannot be less than %{num}", "en.errors.none?"=>"cannot be defined", "en.errors.str?"=>"must be a string", "en.errors.type?"=>"must be %{type}", "en.errors.size?.arg.default"=>"size must be %{size}", "en.errors.size?.arg.range"=>"size must be within %{size_left} - %{size_right}", "en.errors.size?.value.string.arg.default"=>"length must be %{size}", "en.errors.size?.value.string.arg.range"=>"length must be within %{size_left} - %{size_right}"}>, @options={}, @full=false, @hints=true, @locale=:en, @default_lookup_options={:locale=>:en}>, :input_processor=>#<Proc:0x000000000385d1a0@/app/vendor/bundle/ruby/2.3.0/gems/dry-validation-0.11.1/lib/dry/validation/schema/class_interface.rb:11 (lambda)>, :checks=>[]}, @rules={:email=>#<Dry::Logic::Operations::And rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#key?> options={:args=>[:email], :id=>:email}>, #<Dry::Logic::Operations::Key rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#filled?> options={:args=>[]}>] options={:name=>:email, :evaluator=>#<Dry::Logic::Evaluator::Key path=[:email]>, :path=>:email, :id=>:email}>] options={:id=>:email}>, :password=>#<Dry::Logic::Operations::And rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#key?> options={:args=>[:password], :id=>:password}>, #<Dry::Logic::Operations::Key rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#filled?> options={:args=>[]}>] options={:name=>:password, :evaluator=>#<Dry::Logic::Evaluator::Key path=[:password]>, :path=>:password, :id=>:password}>] options={:id=>:password}>}, @checks={}, @executor=#<Dry::Validation::Executor:0x000000000524f8b0 @steps=[#<Dry::Validation::ProcessInput:0x000000000524f860 @processor=#<Proc:0x000000000385d1a0@/app/vendor/bundle/ruby/2.3.0/gems/dry-validation-0.11.1/lib/dry/validation/schema/class_interface.rb:11 (lambda)>>, #<Dry::Validation::ApplyRules:0x000000000524f838 @rules={:email=>#<Dry::Logic::Operations::And rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#key?> options={:args=>[:email], :id=>:email}>, #<Dry::Logic::Operations::Key rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#filled?> options={:args=>[]}>] options={:name=>:email, :evaluator=>#<Dry::Logic::Evaluator::Key path=[:email]>, :path=>:email, :id=>:email}>] options={:id=>:email}>, :password=>#<Dry::Logic::Operations::And rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#key?> options={:args=>[:password], :id=>:password}>, #<Dry::Logic::Operations::Key rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#filled?> options={:args=>[]}>] options={:name=>:password, :evaluator=>#<Dry::Logic::Evaluator::Key path=[:password]>, :path=>:password, :id=>:password}>] options={:id=>:password}>}>], @final=#<Dry::Validation::BuildErrors:0x000000000524f8d8>>>}> >

模型的密码在数据库中没有加密,我相信这是因为Contract :: Build从params而不是模型中获取密码。

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

您应该在合同价值与模型同步之前对其进行加密:

def assign_user_values(options, params:, **)
  options['model'].token = options['data.token']
  options['contract.default'].password = options['data.password']
  options['model'].role = 'user'
end

因为:token和:role不在合约中,你可以直接在模型上设置这些值,但是Contract :: Persist()将同步params中的值,所以改变字段中的值。在同步到模型之前签订合同。

编辑:注意,您也可以在加密密码的方法中执行此操作;无需在步骤之间内部传递数据。