Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
Nick Sutterer
@apotonick
on my list! I actually have a much better idea instead of auto_inject (still using dry-container)
Yogesh Khater
@yogeshjain999

Hey @khataev , I've created a PR for adding test helper which can support mocking a step/nested activity. Let me know your thoughts.

trailblazer/trailblazer-test#14

Yogesh Khater
@yogeshjain999

https://github.com/yogeshjain999/trailblazer-test/tree/mock_step#mock_step

It's just 4-5 lines of code and depends on http://2019.trailblazer.to/2.1/docs/activity.html#activity-dsl-options-patching. So it depends on some latest changes made in trb activity gems.

In case you don't to upgrade your trb stack gems right now, you can define a simple helper for your use. It should to replace the step with your return_value and return new activity for testing.

def mock_step(operation, id, return_value)
  Class.new(operation) { step ->(ctx, **) { return_value }, replace: id, id: id }
end
roma
@milushov

Hello, guys, I'm new in dry-transaction, could you help me to figure out one thing:

I have a few steps in one of the Transaction classes and I'm wondering how to design it better

step :validate_params
map :a - returns extra key "X" in the resulting hash
map :b, with: "shared.b" - (1)
map :c, with: "shared.c" - (1)
step :save - receives and passes "X" further
tee :d - receives and consumes "X"

(1) I dont want to receive this extra key "X" and pass it further here, because I don't need it in other places where I used this shared step

My question -- is there a way not to pass "X" to shared steps, but use "X" in the step "d"? If not, which design to prefer?

Yuriy Stefanyuk
@ystefanyuk

Hello, guys!
I have one question about reform
I have the following form:

module EmployeeRoles
  class CreateForm < BaseForm
    model :employee_role

    properties :name, :description
    validates :name, :description, presence: true

    collection :role_skills, populate_if_empty: RoleSkill do
      include ::NestedForm

      properties :skill_id, :weight
      validates :weight, presence: true, numericality: true

      property :skill do
        properties :name, :description
        validates :name, :description, presence: true
      end
    end
  end
end

And I have to cases:
1) When I can select :skill_id
2) When I create Skill on the fly

EmployeeRole model:

class EmployeeRole < ApplicationRecord
  has_many :role_skills, dependent: :destroy
end

RoleSkill model:

class RoleSkill < ApplicationRecord
  belongs_to :skill
  belongs_to :employee_role
end

And Skill model

class Skill < ApplicationRecord
  has_many :role_skills
end

So basically have to implement not has_one association but belongs_to
How can I achieve this?

Adam Piotrowski
@panSarin
@ystefanyuk could you describe by tests how exactly you want it to behave?
Yuriy Stefanyuk
@ystefanyuk
Thanks @panSarin, I have already figured out how to implement this.
aki1221
@aki1221
Hi,
how to override initialize method with for_collection
currently if i override initalize method inside representor and using representer.for_collection.new(resource) not working while respresenter.new(resource) works fine
Adam Piotrowski
@panSarin
i believe that pasting some example code ( in a form of a gist ) would be easier to understand , so someone would be able to help you with that
@ystefanyuk literally no problem ;D
nickKolya
@nickKolya

Hi everyone! I'm using reform 2.3.0.rc2 + dry-validation 1.4.2 and having a trouble with adding rules that would depend on external data. Is there a possibility to pass model or form instance into the dry validation rule to make some property specific validations? Here is a very stupid example of what I'd like to achieve, but just to give a context:

class ParentRecord < ApplicationRecord
  has_many :percent_records
end

class PercentRecord < ApplicationRecord
  belongs_to :parent_record
end

class PercentRecordForm < Reform::Form
  MAX_PERCENT = 100

  property :value

  validation with: { form: true } do
    required(:value).filled(:integer)

    rule(:value, :number_type) do
      if values[:value] > form.allowed_percent_amount
        key.failure(:too_big_percent_number)
      end
    end
  end

  def allowed_percent_amount
    MAX_PERCENT - model.parent_record.percent_records.sum(:value)
  end
end

Thanks in advance!

paranoid-ninja
@paranoid-ninja
Hello
Any body can help? Method failure uninstalazer
Dusan Orlovic
@duleorlovic
Is there any read world opensource rails app that is using trailblazer ? I was searching https://github.com/eliotsykes/real-world-rails but no success. I would like to see some examples with rails...
Nick Sutterer
@apotonick
@duleorlovic we have some older example apps, but most of us are working on commercial TRB/Rails apps that we can't OSS. Nevertheless, I will finish some (complex!) blog example, at some point
Jester
@Jesterovskiy

Hi everyone! Maybe I miss something from documentation of Representable API, but I can't figure out how I can use a custom dataset for collection. Example:

property :id
property :name
collection :assessments do
  property :total_score
  property :completed_at, getter: ->(represented:, **) { represented.assignments_dataset.last.completed_at }
end

and I have Assessments dataset, that I want to use for collection. How can I do that? Thanks)

Dusan Orlovic
@duleorlovic
@apotonick yea, I also started to work on some commercial app with TRB so that's why I'm interested to learn and analyze other use cases... I found three sites for documentation http://trailblazer.to/guides/
http://2019.trailblazer.to/2.1/docs/trailblazer.html
http://trailblazer.to/api-docs/
so maybe for new comers, we should link them and explain the differences. For documentation I would also suggest to put a links to source because it is much easier to follow if you can see what step Contract::Valiate() is doing... Is there any instructions how to contribute to docs ?
Alex
@legendado
@apotonick Hey, Nick! I have case: when step failed I need to execute another step, but Result must be true. How can I do that?
Yogesh Khater
@yogeshjain999
Hey @legendado Is the "another step" needs be on fail track ?
If not, you can remain on success track even if current step fails like this
step :step_1, Output(:failure) => Id(:step_2)
step :step_2
Zee
@zspencer
Silly question, that may have been answered before; but we are trying to get Representer/ROAR to camelcase the key names of a lonely hash representer that we are using for our errors.
However, we cannot find a seam which would allow us to translate the keys we return when we do not know the property names; which for the ActiveModel::Errors hash will vary on a model-by-model basis.
Does anyone have a recommendation for where to start looking? We are using defaults for our other serializers.
joshudev
@joshudev
Hi @apotonick - please can you advise the status of Roar / Representable? Are they still maintained, or do you have alternatives that should be used for TB2.1?
Vivek Kumar
@viveksingh295_twitter

After upgrading from trailblazer 2.1.0rc1 to 2.1.0 , facing issue while calling another trailblazer operation from within a trailblazer operation. i am getting ArgumentError: wrong number of arguments (given 2, expected 1)

class Name::Operation::Create < Trailblazer::Operation
  pass :label_name_case!

  def label_name_case!(options, params:, **)
    result = AnotherName::Operation::NameVerification.call(options, params: params)
  end
end

was also facing same error while using it within Wrap. any workaround for this ?

Yogesh Khater
@yogeshjain999

Hey @viveksingh295_twitter you can call your OP like this

def label_name_case!(options, **) # removed params as you're not using it
    result = AnotherName::Operation::NameVerification.call(options)
 end

Also, another way is to use https://2019.trailblazer.to/2.1/docs/activity.html#activity-wiring-api-subprocess

  pass Subprocess(AnotherName::Operation::NameVerification)

You're getting this error because of the new signature in 2.1 for calling OPs - We pass single argument (either hash or multiple keyword arguments) while calling and no need to pass params as first argument.

Vivek Kumar
@viveksingh295_twitter
Thanks @yogeshjain999 🙂
Vivek Kumar
@viveksingh295_twitter

In 2.1.0 it seems the options are immutable , any suggestion on how to handle this

class User::Operation::Update < Trailblazer::Operation
  step PhaseMacro::Init
  ...
   fail PhaseMacro::HandleAuthErrors, fail_fast: true
end
module PhaseMacro
  Init = ->((ctx), *, &block) do
    ctx[:status] = :ok
    Trailblazer::Operation::Railway.pass!
  end

  HandleAuthErrors = ->((ctx), *, &block) do
    ctx[:status] = :unauthorized
    ctx[:errors] << { field: '', errors: ['You are not authorized'] }

    Trailblazer::Operation::Railway.fail!
  end
end

on Fail, status is not getting updated, result still contains :ok status
any suggestions?

Yogesh Khater
@yogeshjain999
Well, the status should change as ctx is mutable. Is the flow coming in HandleAuthErrors ?
Small suggestion - callables defined inside PhaseMacro aren't actually behaving like Macro. Maybe you should rename it to not have macro in its name.
Vivek Kumar
@viveksingh295_twitter
@yogeshjain999 yes flow is coming in HandleAuthErrors
Vivek Kumar
@viveksingh295_twitter

@yogeshjain999 it looks like the issue is with Wrap, HandleAuthErrors is being called within

Wrap(TransactionWrapper) do
...
fail PhaseMacro::HandleAuthErrors, fail_fast: true
....
end
TransactionWrapper = Class.new do
    def self.call((ctx), *, &block)
      result = ActiveRecord::Base.transaction { yield }
      ...
      Trailblazer::Operation::Railway.pass!
    end
end

I tried removing the Wrap and got the tests passing, any suggestions to fix this?

Yogesh Khater
@yogeshjain999
Hey @viveksingh295_twitter it seems you're always returning Trailblazer::Operation::Railway.pass! from the wrapper ??
Vivek Kumar
@viveksingh295_twitter
No, I have implemented the logic to return pass and fail based on result.
I tried checking the value of ctx before and after result = ActiveRecord::Base.transaction { yield }
ctx[:errors] is updated but ctx[:status] remains same :ok
Yogesh Khater
@yogeshjain999

In the new version, the signature of result is bit changed (yet to be released in documentation).

You should change
result = ActiveRecord::Base.transaction { yield }

to
signal, (ctx, flow_options) = ActiveRecord::Base.transaction { yield }
to resemble with 2.1 signature of steps.

Based on the signal value, you should return pass or fail from the transaction.

Let me know if you find any difficulties in this!
Vivek Kumar
@viveksingh295_twitter
Updated the result signature but still the same issue
Yogesh Khater
@yogeshjain999
Hmm, Is it possible to post your full example ?
Vivek Kumar
@viveksingh295_twitter

In Controller

def update_language
    authorize current_user, :update_language?

    result = User::Operation::UpdateLanguage.call(
      params: user_language_params,
      current_user: current_user
    )
    render_result(result)
end

def render_result(result)
    if result.success?
      render status: result[:status],
             jsonapi: result[:model]
      return
    end

    render status: result[:status], json: result[:errors].to_json
end

In Operation

class User::Operation::UpdateLanguage < Trailblazer::Operation
  step StepMacro::Init
  step(Wrap(StepMacro::TransactionWrapper) do
    step Model(User, :find_by)
    step Policy::Pundit(UserPolicy, :update_language?)
    fail StepMacro::HandleAuthErrors, fail_fast: true
    step ->(options, model:, **) { !model.internal? }
    pass :build_user_attributes
    step :update_user
    fail :handle_error
  end)

In Macro

module StepMacro
  Init = ->((ctx), *, &block) do
    ctx[:errors] = []
    ctx[:status] = :ok
    ctx[:current_user] ||= User.new

    Trailblazer::Operation::Railway.pass!
  end

  TransactionWrapper = Class.new do
    def self.call((ctx), *, &block)
      signal, (ctx, flow_options) = ActiveRecord::Base.transaction { yield }
      success = signal.inspect.eql?(%(#<Trailblazer::Activity::End semantic=:success>))
      success ? Trailblazer::Operation::Railway.pass! : Trailblazer::Operation::Railway.fail!
    end
  end
end

Checked in controller result[:status] is still :ok

Vivek Kumar
@viveksingh295_twitter
module StepMacro
  HandleAuthErrors = ->((ctx), *, &block) do
      ctx[:status] = :unauthorized
      ctx[:errors] << { field: '', errors: ['You are not authorized to perform this action.'] }

      Trailblazer::Operation::Railway.fail!
  end
end
Yogesh Khater
@yogeshjain999

Found the issue.

Wrap callable should also return it's local ctx (returned by yield), so that it gets considered in steps outside. This is again to be compatible with new style and allow more control to Wrap.

So your TransactionWrapper should look like this

TransactionWrapper = Class.new do
    def self.call((ctx), *, &block)
        signal, (ctx, flow_options) = ActiveRecord::Base.transaction { yield }
        success = signal.inspect.eql?(%(#<Trailblazer::Activity::End semantic=:success>))
        signal = success ? Trailblazer::Operation::Railway.pass! : Trailblazer::Operation::Railway.fail!

        [signal, [ctx, flow_options]]
  end
end
Vivek Kumar
@viveksingh295_twitter

@yogeshjain999 Thanks a lot , updating the return type of callbale fixed that issue 🙂 .
but these changes raised other issue , ActiveRecord::Base.transaction { yield } return nil in case of Exception(In one of my steps raise ActiveRecord::Rollback was used). fixed that by updating TransactionWrapper return

[signal, [ctx || options, flow_options]]

where options is the parameter for TransactionWrapper #call

Yogesh Khater
@yogeshjain999
oh nice!!
Dusan Orlovic
@duleorlovic_gitlab
Just to note that I'm on half way to finish adapting gemgem application form the Trailblazer book on Rails 6 https://gitlab.com/duleorlovic/gemgem-trb-rails-6 (I do not like MS globalization so I chose Gitlab). I skipped callbacks part since I have not found that in trb 2.1 (let me know if there is something similar) I hope it will help new trailblazers to follow the book.
corehook
@corehook
Hi all! i need help with passing current_resource to Contract for doing some validations. i call my operation Mode::Operation::Sample.call(params: {}, current_resource: user)
and want touch current_resource model in Contract. How i can do it ?
Yogesh Khater
@yogeshjain999
@corehook Is it possible to run touch in Operation itself after the validation ran successfully ?
corehook
@corehook
sorry , i mistake, i just want get my current user id
in contract
corehook
@corehook
если кому-нибудь понадобиться, то вот так я закостылял
# frozen_string_literal: true

module SomeModel::Operation
  class Destroy < Trailblazer::Operation
    step :model!
    step Contract::Build(constant: ::SomeModel::Contract::Destroy)
    step Contract::Validate()
    step :check_errors
    step :destroy!

    def model!(options, params:, current_resource:, **)
      options['model'] = {
        some_model: SomeModel.find(params[:id]),
        current_resource: current_resource
      }
    end

    def check_errors(options, model:, **)
      options['model'] = model[:some_model]
    end

    def destroy!(_options, model:, **)
      return false unless model.errors.empty?
      model.update(removed: true)
    end
  end
end
Yogesh Khater
@yogeshjain999
Hmm, so you can have a property as current_user_id in ::SomeModel::Contract::Destroy and pass it via params ?
Form params should include all the params needed in contract (as an hidden field if it's HTML form).
That way, you won't need to have custom model! step and you can use standard macros instead.