by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
Yuriy Tsemashko
@azzz

Hi there. What is a better way to work with mongodb in hanami? I found hanami-mongoid gem that implements mongoid-based repositories but the gem has not been updated for 15 months already.

Of course I can get rid off hanami models and repositories and use mongoid directly but then I have concerns why I can't use just sinatra

Armin
@wuarmin
@DannySantos here's the commit at rack, where they moved from Time.now to a monotonic clock: rack/rack@743f29d
With that Hanami's CommonLogger cannot calculate elapsed properly:
      now    = Time.now
      length = extract_content_length(header)

      msg = Hash[
        http:    env[HTTP_VERSION],
        verb:    env[REQUEST_METHOD],
        status:  status.to_s[0..3],
        ip:      env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR],
        path:    env[SCRIPT_NAME] + env[PATH_INFO].to_s,
        length:  length,
        params:  extract_params(env),
        elapsed: now - began_at #calculation fails
      ]
Armin
@wuarmin
we should move to:
now    = Rack::Utils.clock_time
Armin
@wuarmin
@azzz Checkout the hanami-mongoid gem. It is a very small gem. Maybe it helps you. If not, just use mongoid directly (like in Sinatra). Hanami is much more, than hanami model.
Armin
@wuarmin
I opened a PR (hanami/hanami#1061)
Danny Santos
@DannySantos
@wuarmin Great find! Maybe open an issue on the Hanami::Utils Github?
Armin
@wuarmin
@DannySantos I opened a PR
Danny Santos
@DannySantos
👍🏼
Armin
@wuarmin

@DannySantos do you have an answer to this question?

Hey, another question: Puma-thread-count should correspond to the connection-pool size. How I can define the connection-pool-size of Hanami-model (romrb, sequel under the hood)?

Danny Santos
@DannySantos
@wuarmin I think if you use the postgres URI format you can pass in additional parameters, maybe see if you can configure it there?
Armin
@wuarmin
@DannySantos you are right: hanami/model#168. Thanks!
Sebastian Nowak
@seban
I have one question regarding explicit return in interactors call methods. Is something like return unless something can be considered as good practice? Would it be better to fail! unless something?
Armin
@wuarmin
@seban I would use error! or fail! http://hanamirb.org/docs/1.0.0/Hanami/Interactor.html
Sebastian Nowak
@seban
Yeah, it looks like better option. Otherwise there is always successful? result returned. Thans Armin!
Danny Santos
@DannySantos
@seban Even better, you can integrate dry-monad and always return a monad, whether it fails or succeeds.
A little more complex to set up but you can get a really nice interactor pattern using monads
Sebastian Nowak
@seban
@DannySantos any valuable example?
Danny Santos
@DannySantos

@seban This article goes into it slightly: https://medium.com/adhawk-engineering/designing-services-with-dry-rb-fe850f8dd4b7

But it doesn’t describe how you can handle various results in your controller by calling your interactor with a block:

interactor.call(params) do |m|
  m.success do |user, msg|
    flash[:notice] = msg
    redirect_to routes.user_path(@user.id)
  end
  m.failure(:auth_error) do |user, msg|
    flash[:notice] = msg
    redirect_to routes.auth_error_path
  end
  m.failure do |user, msg|
    flash[:notice] = msg
    redirect_to new_session_path
  end
end

It’s a really great way to handle interactor results, sorry I don’t have a more in-depth example for you but you could google around if you’re interested in using it.

Sebastian Nowak
@seban
Ok, now it is clear what you wanted to say. I think I saw a similar approach in wisper gem. Thank for article will read it :bow:
Danny Santos
@DannySantos
👍🏼
Masanori Ohnishi
@MasanoriOnishi

I have a question.
hanami/model#294

How does everyone avoid creating anemic domain model ?
Although entities are the core of an application and should have methods related to domain logic, we can't add update method to them, because they are immutable objects.

In this article, he says that you should use repository for update process, but does it seem to be outflow of domain logic? ("anemic domain model")

Sebastjan Hribar
@sebastjan-hribar

I have a follow-up problem to my aggregate. The full gist is still here .
For the aggregate below I would like to filter by the translation_record's attribute as well. To get only records for a given language combination. I need this, because, as I stated before, the aliased self-relational association doesn't work yet in this version of Hanami Model.

class SegmentRepository < Hanami::Repository

  associations do
    has_many :translation_records
    has_many :segments, through: :translation_records, as: :target_segments
    belongs_to :language
  end

  def find_by_segment_match(source_text_for_lookup, source_lang, target_lang, sim_score, max_results)
    aggregate(:translation_records)
      .where {similarity(:content, source_text_for_lookup) > sim_score/100.00}
      .select_append {float::similarity(:content, source_text_for_lookup).as(:similarity)}
      .order {similarity(:content, source_text_for_lookup).desc}
      .limit(max_results)
  end

end

I've added a language_combination attribute to translation_records.

I want to do something like .where {translation_records.language_combination = "#{source_lang}_#{target_lang}". This yields: undefined method meta for TranslationRecord:Class

ippachi
@ippachi

@sebastjan-hribar

class UserRepository
  associations do
    has_many :posts
  end

  def find_with_posts_by_created_at(user_id, from: nil, by: nil)
    aggregate(:posts)
      .where(users[:id].qualified.is(id))
      .node(:posts) do |posts|
        posts.where(post_created_at_gt(from))
             .where(post_created_at_lt(by))
             .order(post_created_at_desc)
      end
      .map_to(User).one
  end

  private

  def post_created_at_gt(from)
    (posts[:created_at].qualified > DateTime.parse(from).to_time + 1)
  end

  def post_created_at_lt(by)
    (posts[:created_at].qualified < DateTime.parse(by).to_time + 1)
  end

  def post_created_at_desc
    posts[:created_at].qualified.desc
  end
end

Does this code help you?
I found node method at rom-rb documentation.
https://api.rom-rb.org/rom/ROM/Relation/Combined#node-instance_method

Sebastjan Hribar
@sebastjan-hribar
@ippachi YES! Thank you. :)
This works great:
def find_by_segment_match(source_text_for_lookup, source_lang, target_lang, sim_score, max_results)
    aggregate(:translation_records)
      .node(:translation_records) do |translation_records|
        translation_records.where(language_combination: "#{source_lang}_#{target_lang}")
      end
      .where {similarity(:content, source_text_for_lookup) > sim_score/100.00}
      .select_append {float::similarity(:content, source_text_for_lookup).as(:similarity)}
      .order {similarity(:content, source_text_for_lookup).desc}
  end
Matt Culpepper
@mculp
@ippachi thanks, that also helped me figure out the question I came to ask, lol
Sebastjan Hribar
@sebastjan-hribar
@ippachi :) that's great, but I spoke a bit too soon. The above code doesn't yield only segments for the .node condition of translation records, but I get back all segments. So I need to look how to change this. But still, I can access the translation_records.
Johannes von Bargen
@prosac
hi! a colleague at work implemented an internal app using hanami. since we are asked to run ruby 2.7 upwards he now seems to plan to port the app to rails. i would like to determine now how the opinions are regarding hanami on 2.7. running script/ci looks not too bad. just a lot of deprecation warnings regardning the changes in rubys argument handling (https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/). i also gave the tests of hanami/utils a try. similar outcome (making a quick spike to see if i could maybe get that fixed right now). but before i go on and run in the wrong direction: are there any ongoing efforts on porting hanami and its libs to ruby 2.7 as of now?
Viktor
@skcc321
I have the same question. There is not much contribution to hanami on Github, so people start afraid of using hanami because it can become unmaintained soon. If they could do some scheduled releases it would bring some confidence in the framework... But no... So looks like here is the only way - switch back to the worse arch but regularly updated rails...
Paweł Świątkowski
@katafrakt
Hanami 2.0 will work in 2.7 for sure. It's true that all efforts now go into it and 1.x feels a bit abandoned.
Viktor
@skcc321
do we have any ETA when 2.0 is gonna be released?
Jeff Dickey
@jdickey
...or at least with a beta-quality release that has a consistent set of Gems working together? Playing Whac-A-Mole® on 1.3.3 right now, and it's been going slowly enough that we've been tasked with reimplementing our original single-small-service Hanami code for the current project in Roda+dry-rb+sequel and Rails for comparative evaluation. If I can credibly say that our Hanami pain points are likely to be addressed by (e.g.) mid-August, we might be able to avoid all the drama
Paweł Świątkowski
@katafrakt
I've heard end of year as a possible date.
Jeff Dickey
@jdickey
Thanks, @katafrakt; that's what I'd previously understood and been afraid of. I love this framework, but I can't keep selling it as something we can rely on for non-trivial revenue apps at present without falling back to 1.2.x, and even that would have us reimplement or work around too many tools/Gems that are production-ready in other ecosystems, notably Rails. I'll still keep hacking on Hanami stuff on my own time as I get some once my current health issues are sorted, but I can't in good faith keep doing the Sisyphus routine over here for the day job. I'll pass this upstairs with my recommendations, and feast on cold, stale crow for the next month or two until I can show progress against the backlog. Thanks again.
kristjan-brezovnik
@kristjan-brezovnik
Hi!
Does Hanami javascript helper support JS modules? If so, how can I specify that a JS file is a module?
Kai Kuchenbecker
@kaikuchn

Hey @kristjan-brezovnik can you elaborate on what you want to achieve? I'm guessing that you'd like to include only specific parts of a module? If so, I don't think this is possible. If I'm not mistaken all the helpers do is write the correct script tags for you. I.e., they take care of the changing fingerprints of assets for you, but otherwise there is no magic involved of any kind. See https://github.com/hanami/assets#helpers and https://github.com/hanami/assets/blob/master/lib/hanami/assets/helpers.rb#L85 for more details.

However, you can write your own helper. Depending on what it is that you need it could look something like this?

require 'hanami/helpers/html_helper'

def load_js_module(module_name)
  file_name = module_name + Hanami::Assets::Helpers::JAVASCRIPT_EXT
  html.script(type: "module", src: asset_path(file_name, push: false, as: nil))
end

When called in a template with load_js_module("main") This would generate tags like this <script type="module" src="main.js"></script>.
You could also implement the helper that would yield the asset path to a block and then in your template do some stuff in the block. Like this:

require 'hanami/helpers/html_helper'

def load_js_module(module_name)
  file_name = module_name + Hanami::Assets::Helpers::JAVASCRIPT_EXT
  html.script(type: Hanami::Assets::Helpers::JAVASCRIPT_MIME_TYPE) do
    yield asset_path(file_name, push: false, as: nil)
  end
end

And then

<%= load_js_module("main") do |module_path| %>
  // your javascript code here, something like import '..' from <%= module_path %>
<% end %>

These are all rough ideas, so don't expect there to not be any typos etc. :) Hope this helps somewhat though.

Man, I love Ruby. At my new Job I'm only using Elixir now - which is a damn great language - but Ruby is still my favorite. (:
Sebastian Nowak
@seban
Is it possible to do OR query SELECT * FROM foo WHERE blah = 1 OR sth = 2 with Hanami repository?
Sebastian Nowak
@seban
Ok, looks like I figure it out. SQL OR can be done by single pipe | .where { conditon_a | condiion_b }
kristjan-brezovnik
@kristjan-brezovnik
@kaikuchn Wow, thanks for such detailed answer.:D
Sebastian Nowak
@seban
Is it possible to use partial templates in mailer templates?
Matt Culpepper
@mculp

What would be the best way to explicitly load an association with a query such as this:

SELECT
  *
FROM (
  SELECT
    activities.*,
    row_number() OVER (PARTITION BY company_id ORDER BY created_at DESC) n
  FROM activities) limited_activities
WHERE
  n <= 2
ORDER BY
  company_id
LIMIT 20;

Basically, I want to load all companies and return each company's latest two activities.

How would I write a method in my CompanyRepository to do this?

Sebastjan Hribar
@sebastjan-hribar
@mculp Wouldn't aggregate and possibly nodeas in my example above work? My complete setup for self-referential association is still here. I haven't changed anything apart from trying node.
Matt Culpepper
@mculp
@sebastjan-hribar I don't think you can limit the associated records in that way. I ended up creating a Postgres view with the data I needed plus an empty repo + entity and it worked like a charm.
Sebastjan Hribar
@sebastjan-hribar
@mculp Thank you for the info on your solution.
Sebastjan Hribar
@sebastjan-hribar
I made a simple custom rake task to populate the DB and I followed the guides (https://guides.hanamirb.org/projects/rake/#environment). I get the error that the repository is not initialized. Any idea?
Erik Clarizio
@eclarizio

Hey all, I might be using the wrong pattern here, but I'm curious if anyone else has any interactors that utilize other interactors as a way to separate concerns of a larger piece of work, and if so, how do you handle it from the spec side when exposing attributes. As an example, I have an interactor that processes a CSV file, and has expose :parsed_csv_data. So the actual call ends up being something like @csv_processor.call(filename).parsed_csv_data. In the spec:

let(:csv_processor) { instance_double(CsvProcessor) }

before do
  allow(csv_processor).to receive(:call).with(filename).and_return(????)
  # Not sure what goes above, because it needs to be an object that then
  # responds to .parsed_csv_data, but neither the CsvProcessor or a
  # Hanami::Interactor do, it's only exposed via the expose keyword
end

Should I just be using plain old ruby objects and regular attr_readers, I guess?

Sebastjan Hribar
@sebastjan-hribar
RE custom rake tasks: in task :my_super_task do I had to provide the :environment as well: task my_super_task: :environment do.
Armin
@wuarmin
@eclarizio Why you want to double your CsvProcessor?
   allow(csv_processor).to receive(:call).with(filename) do
      result = double(Hanami::Interactor::Result)
      expect(result).to receive(:success?).and_return(true)
      expect(result).to receive(:warnings).and_return(['this is a test warning'])
      expect(result).to receive(:parsed_csv_data).and_return(...)
      result
    end
Erik Clarizio
@eclarizio

@wuarmin Following the dependency injection, the spec would then use the CsvProcessor double as an argument to the .new method. I forgot you could use the block syntax for .with so that might work or at least help a bit. Basically I'm thinking if you had something like this:

# SomeAggregatorInteractor
def initialize(csv_processor = CsvProcessor.new, other_processor = OtherProcessor.new)
  @csv_processor = csv_processor
  @other_processor = other_processor
end

def call(csv_filename)
  parsed_data = @csv_processor.call(csv_filename).parsed_csv_data
  @other_processor.call(parsed_data).some_important_result
end

In the spec you need a way to get the value from .parsed_csv_data, but you can't assign it directly on the instance_double because that method doesn't exist for a CsvProcessor. I could just use a double I guess, but the instance_double is super nice for verifying that the method I'm attempting to call is a valid method. If I accidentally misspell :parsed_csv_data in both the expectation and the code, I won't know until runtime. I was hoping to be able to preserve that while also including the Hanami::Interactor module, but like I said maybe I'm using the wrong pattern and interactors aren't meant to be calling other interactors. Or maybe they're supposed to be more like "do this work and I don't care much about the result". For now I've just resorted to doing attr_reader :parsed_csv_data instead of expose :parsed_csv_data and then simply returning self from the #call method.