First off - I wouldn't use HABTM here, but instead a named join table (called Relationship) with it's own model and use has_many :people, through: :relationships.
- I have created 2 people. I want the second person to be included when I query for Person.first.people and vice versa! (i.e the first person shall be included when I query for Person.second.people). I was close to achieve this with has_and_belongs_to_many :(people|reversed_people): https://stackoverflow.com/a/46230787/6030239
You can achieve this with a single activerecord relationship (either has-many through or HABTM) by adding two rows for each relationship (one in each direction). For example:
def add_bi_directional_relationship(first:, second:, type:)
Relation.create!(person_a: first, person_b: second, connection: type)
Relation.create!(person_a: second, person_b: first, connection: type)
end
# first.people => [second]
# second.people => [first]
Activerecord associations are designed to query a table by a foreign key, so to use them in a straightforward way you need a table where the value you want to query is going to be in a single column.
However, why do you need it to be done via an activerecord association? You can write a method that does the query you need.
class Person
has_many :forward_people, through: :relations, #...
has_many :reversed_people, through: :relations, #...
def people
forward_people + reversed_people
# or build the joins in SQL strings or Arel
end
end
Or lots of other potential solutions, depending on your needs.
- Relationship which connects the two has a connection value of friends. I want to create a has_many :friends method, such that the second person will appear in Person.first.friends query and vice versa!
Not sure I totally understand this... but if you take the first approach of adding a relationship for each direction, then you can write a method like:
def friends
people.where(relations: {connection: 'friends'})
end
You can also do this with another activerecord relationship... but I wouldn't recommend defining multiple activerecord associations for the same foreign keys. An alternative approach could be to define a .friends scope that would allow you to do Person.first.people.friends.
In general these goals are difficult to accomplish because you're specifying an implementation (an activerecord association that returns specific values) without indicating why you need it to be done like / what problems you're trying to solve with it. Activerecord associations are helpful to a point, but they also introduce a layer of abstraction that can add complexity/confusion. Discussing the actual business/application needs you want to solve (e.g. a report that shows X, a view that renders X, or a form that saves X) would allow people to suggest alternative approaches that might be a better fit what you're trying to accomplish.