2

Can't find the configuration or setting for this (already googled several times). When inspecting some model objects in Rails console, why is it sometimes the model fields are displayed alphabetically and most times in other deployments, it does not?

Example:

### FIELDS NOT SORTED
#rails console
(main)> l = Loan.find '123' 
 => #<Loan:0x000000066722e8
 id: "8f196106c00e",
 user_id: "f90084a53972",
 borrower_id: "043bb77b3aac",
 parent_id: nil,
 score_id: "f00c39e74570",
 number: 11231321,
 scoring_tag: nil,
 .....

but in other deployments, when I go in rails console

 # FIELDS SORTED ALPHABETICALLY
 (main)> l = Loan.find '123'
  => #<Loan:0x007fca8b45a468
  active_servicer_id: nil,
  amortization: nil,
  amount: 150000.0 (BigDecimal),
  application_fee: nil,
  borrower_id: "asdasdajdjasd",
  borrower_requested_closing_attorney: nil,
  channel: nil,
  closed_date: nil,
  commitment_end_at: nil,

How can I make rails console display output of models sorted? This is sometimes necessary when comparing two records.

ibaralf
  • 12,218
  • 5
  • 47
  • 69
  • I don't think such a config option exists, you may want to check the inspect method of `ActiveRecord::Base`. Have you tried converting the record to hash and sorting it alphabetically? – Tamer Shlash Oct 11 '18 at 22:54
  • Yes, I can do that, but I wanted to see if there was something so that I don't have to do it every time I log into a different server. – ibaralf Oct 11 '18 at 22:57
  • Why is this something you want/need to do? – lacostenycoder Oct 11 '18 at 23:47
  • 1
    Side note, my coworker showed my team this gem this week. You might find it helpful. https://github.com/awesome-print/awesome_print/blob/master/README.md – Nate Oct 12 '18 at 03:47

2 Answers2

2

If you just want to print out the attributes, you can do that more explicitly:

Loan.find(123).attributes.sort_by(&:first).to_h

If you really want the console print to do that, you may want to override the inspect method. At least for irb and some other consoles, this is the method that determines what gets printed. If you have some other plugin or gem that the console is using, you may have to look at the docs to find the appropriate method.

Something like (modify as you wish, e.g. to better mimic the default inspect, display object_id, etc)

class Loan ...
  def inspect
    (self.class.name + ": " + attributes.sort_by(&:first).to_h).to_s
  end
end
Andrew Schwartz
  • 4,440
  • 3
  • 25
  • 58
  • To really mimic the default `inpsect` you need to get the object id as it does: see https://stackoverflow.com/questions/2818602/in-ruby-why-does-inspect-print-out-some-kind-of-object-id-which-is-different – Andrew Schwartz Oct 12 '18 at 03:14
0

In Ruby's Hash, the order of its elements is arbitrary. When it is inspect-ed, (as in the output of rails console), the method inspect just uses each_pair or something similar, and so the order can be arbitrary. In practice, the order for Hash#each_pair usually seems to follow the order each Hash element is created; however there is no guarantee! Also, how Rails (or ActiveRecord) creates those instances is another issue (I guess the alphabetical order you see originates how Rails created them; it may have something to do with how they are stored in the DB). Anyway, the bottom line is you can not control how they are stored, as long as a Hash is used. But you can tweak how they are inspect-ed.

The following strategy is to redefine ApplicationRecord#inspect for any of its child classes so the inspect method of any Models are modified but of none of any other classes.

Put the following code in whatever file in your app/ directory (e.g., an obvious filename is /app/models/application_record.rb):

class ApplicationRecord
  alias_method :inspect_orig, :inspect if ! self.method_defined?(:inspect_orig)
  def inspect
    klass = self.class 
    if klass == ApplicationRecord
      return inspect_orig
    end
    attr_list = attribute_names.sort.map { |name| "#{name}: "+attributes[name].inspect }.join ', '
    sprintf('<%s:0x%s %s>', klass.name, object_id.to_s(16), attr_list)
  end
end

Or, instead you can run it every time you open rails console if you want to activate it only temporarily during the session.

Note the name of the parent class of ApplicationRecord may be added as follows (in the case of Rails 5.2; but I don't think inclusion of the parent class is mandatory, for the class ApplicationRecord should be defined before the file is read).

class ApplicationRecord < ActiveRecord::Base

I notice your rails console outputs look slightly different from my Rails 5.2.1, maybe due to the difference in the versions(?). In the code snippet above, I have tried to mimic the outputs you seem to get. Anyway adjust it to your liking; now you have a total control on it!

EDIT1:
The description above applies to Rails 5. In Rails 4, the class name should be

class ActiveRecord::Base

EDIT2:
In the code snippet above, it shows #object_id (n.b., it is not displayed in Rails 5.2). However, what #inspect shows for Object instances is different from #object_id, as explained in detail in this answer. If you prefer to display it, a way is this (the left side value is the format for sprintf):

'0x%016x' % (object_id << 1)
Masa Sakano
  • 1,921
  • 20
  • 32