Giter Club home page Giter Club logo

Comments (11)

zw963 avatar zw963 commented on July 3, 2024 1

Thanks a lot for explain, i will use this gem several days, and switch all my service from interactor to it.

If still have question, i will discuss here.

Thank you.

from actor.

zw963 avatar zw963 commented on July 3, 2024 1

For #2 you could accept several types and override the handler method to transform into an integer:

It works!

input :stock_id, type: Integer, allow_nil: false

This way raise error when user not pass :stock_id in.

ServiceActor::ArgumentError: Input insider_id on RetrieveInsiderHistory is missing

so, the only way is use default: nil, and business logic is decide use if :stock_id inside service object.

from actor.

sunny avatar sunny commented on July 3, 2024

Hi @zw963!

In Actor, the defaults are just like the default keyword values in Ruby. E.g.:

def serve_dish(name: "fries")
  puts "Enjoy your #{name.inspect}"
end

serve_dish
# => Enjoy your "fries"
serve_dish(name: nil)
# => Enjoy your nil

Acts the same way as:

class ServeDish < Actor
  input :name, default: "fries"

  def call
    puts "Enjoy your #{name.inspect}"
  end
end

ServeDish.()
# => Enjoy your "fries"
ServeDish.(name: nil)
# => Enjoy your nil

In cases like these using compact before passing nil argument works well, like you did.

If you’d like to treat nil as being the default, I guess you could also do this like so:

class ServeDish < Actor
  input :name, default: nil

  def call
    puts "Enjoy your #{name.inspect}"
  end

  def name
    result.name.nil? ? "fries" : result.name
  end
end

ServeDish.()
# => Enjoy your "fries"
ServeDish.(name: nil)
# => Enjoy your "fries"

But your solution with slice seems much better in that case!

from actor.

zw963 avatar zw963 commented on July 3, 2024

Thank you for excellent explain.

But, my slice solution exists another issue.

Let's us said we have a service like this, stock_name is not always exists.

class RetrieveStocks < Actor
  input :sort_column, default: 'next_earnings_date'
  input :sort_direction, default: 'asc'
  input :page, default: '1'
  input :per, default: '20'
  input :stock_name

  def call
  # ...
  end
end

Assume invoke this service like this:

RetriveStocks.call, it will raise error because
stock_name no default value.

But, for my service logic, if no stock_name provided, it should not
search base on stock_name instead, the only solution is, give nil as default value,

class RetrieveStocks < Actor
  input :sort_column, default: 'next_earnings_date'
  input :sort_direction, default: 'asc'
  input :page, default: '1'
  input :per, default: '20'
  input :stock_name, default: nil

  def call
  # ...
  end
end

But, this means, all input 'MUST' have default value, so, here is a choice dilemma ...

if pass in all, will meet passed in nil issue.
if not pass in(as use slice), have to add default: option to all input ...

from actor.

sunny avatar sunny commented on July 3, 2024

To disallow nil you can actually use the allow_nil option or even the type option. For example:

class RetrieveStocks < Actor
  input :stock_name, default: nil, allow_nil: false
  input :sort_column, default: 'next_earnings_date', type: 'String'

  def call
    puts "Stock name: #{stock_name}, Sort: #{sort_column}"
  end
end

RetrieveStocks.call
# => ServiceActor::ArgumentError: The input "stock_name" on RetrieveStocks does not allow nil values

RetrieveStocks.call(stock_name: nil)
# => ServiceActor::ArgumentError: The input "stock_name" on RetrieveStocks does not allow nil values

RetrieveStocks.call(stock_name: "Apple", sort_column: nil)
# => ServiceActor::ArgumentError: The input "sort_column" on RetrieveStocks does not allow nil values

RetrieveStocks.call(stock_name: "Apple")
# => Stock name: Apple, Sort: next_earnings_date

from actor.

zw963 avatar zw963 commented on July 3, 2024

After switch to several of my service to actor, following is some note:

  1. When i invoke service from roda router, i can use like following directly. (no need slice or compact)
result = RetrieveInsiderHistory.call(r.params)

because it seem like actor can assure if one arg not statement use input, it not accessable in service object.
and r.params never return return value as nil

  1. most of my input can not specify type, because those args come from params, as following:
class RetrieveInsiderHistory < Actor
  input :days, default: '7'
  input :sort_column, default: 'date'
  input :sort_direction, default: 'desc'
  input :stock_name, default: nil
  input :stock_id, default: nil, type: Integer
  input :insider_id, default: nil, type: Integer

in fact, days should be a integer, but i have to use string instead, because, if specify type: Integer for days, i have to handle it
before pass to it., like this:

days = r.params['days']
if days.present?
  days = days.to_i
else
  days = 7
end

result = RetrieveInsiderHistory.call(days: days)


that not good, because i have to write dirty logic outside service object for make type: Integer can pass.
I really should write those logic in service object instead, 

e.g  if we have a handler option:

```rb
input :days, handler: ->  { if _1.present? ? _1.to_i : 7 }
  1. i have to specify default option for all my input, even, as following case
 input :stock_id, default: nil, type: Integer

I have to specify default: nil + type: integer for my logic still work.

because if for cases user not passed in :stock_id in params, i need avoid raise error, and use nil in service for determinate.

Anyway, i don't know if i misunderstood or misusage somethings, it a little strange ...

from actor.

sunny avatar sunny commented on July 3, 2024

For #2 you could accept several types and override the handler method to transform into an integer:

class RetrieveInsiderHistory < Actor
  input :days, default: 7, type: [Integer, String]
  
  def call
    puts "#{days.inspect} days"
  end

  private

  def days
    result.days.to_i
  end
end

RetrieveInsiderHistory.call
#  => "7 days"

RetrieveInsiderHistory.call(days: 99)
RetrieveInsiderHistory.call(days: "99")
#  => "99 days"

For #3 it’s because when you are asking for a specific type it assumes you want this type and don’t want nils. So you could write it any of these two ways:

input :stock_id, type: Integer, default: nil
# or
input :stock_id, type: Integer, allow_nil: false

from actor.

zw963 avatar zw963 commented on July 3, 2024

I switch all my service to use actor now.

Finally, i add all my input with type and default option,like this

class RetrieveInsiderHistory < Actor
  input :days, default: 7, type: [Integer, String]
  input :sort_column, default: 'date', type: String
  input :sort_direction, default: 'desc', type: String
  input :stock_name, default: nil, type: String
  input :stock_id, default: nil, type: Integer
  input :insider_id, default: nil, type: Integer
  # ...
end
 

a little tediously, but, anyway, it useful, and more clear for find out what service object accepted.

There still a issue, when i run test, get many warn message like this:

/home/zw963/Stocks/marketbet_crawler/app/services/retrieve_insider_history.rb:97: warning: method redefined; discarding old days
/home/zw963/others/.rvm/gems/ruby-3.0.2@marketbet_crawler/gems/service_actor-3.1.2/lib/service_actor/attributable.rb:26: warning: previous definition of days was here

If this gem want user override exists same name method this way, it should do some hack for avoid produce those warning.
the offical recommand way for this is to add a alias. as following code, (i copy from sequel gem).

you can check discuss here.

https://groups.google.com/g/sequel-talk/c/fwa1Pwye_CY/m/DANKiBGOBQAJ

from actor.

zw963 avatar zw963 commented on July 3, 2024

Another issue, please check following example.

# i assign result.institutions = ??? in this service.
result = RetrieveInstitutions.result(params)

if result.success?
    @institutions = result.institutions1 # But i never defined this method in above service, but when invoke it, not raise any error, just return nil, this is not acceptable?
end

Above result.institutions1 return nil, which make result.success? almost no any means for this case.

from actor.

sunny avatar sunny commented on July 3, 2024

If this gem want user override exists same name method this way, it should do some hack for avoid produce those warning.

Thank for the link, didn’t know about that hack! I’d be happy to accept a patch with a test that could remove this warning.

But when invoke it, not raise any error, just return nil

Indeed, that is a side effect of using an OpenStruct to store the set of results. I’d be happy for a patch that would raise an error in that case.

from actor.

zw963 avatar zw963 commented on July 3, 2024

let us discuss another issue in new issue.

from actor.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.