autoload_paths not aware of namespace? - ruby-on-rails

In app/services, I have some classes, as Notification::Finder and Notification::Builder.
They are placed as app/services/notification/builder.rb and app/services/notification/finder.rb.
There is also the Notification class as a model, at app/models/notification.rb
The autoload_path is configurated as in config.autoload_paths += %W(#{config.root}/app/services)
When I try to load Finder, it works:
Loading development environment (Rails 3.2.9)
[1] pry(main)> Notification::Finder
=> Notification::Finder
But when I try the Builder, I get a problem with the rails autoloading:
Loading development environment (Rails 3.2.9)
[1] pry(main)> Notification::Builder
=> ActiveRecord::Associations::Builder
It just ignores the namespace I've used when the constant name (Builder) has already been defined by other namespace, and gets the ActiveRecord::Associations::Builder instead.
Is this the expected behavior, or a rails bug?
Going more detailed, the const_missing method at activesupport/dependencies.rb receives a const_name 'Builder', and nesting.inspect => 'nil'.
Curious that when I use constantize, it resolves as expected:
Loading development environment (Rails 3.2.9)
[1] pry(main)> 'Notification::Builder'.constantize
=> Notification::Builder
( Rails issue at github: https://github.com/rails/rails/issues/8726 )

ActiveRecord::Associations::Builder is a module in Rails. If you have a Notification::Builder, you can ask it its class:
>> Notification::Builder
=> ActiveRecord::Associations::Builder
>> Notification::Builder.class
=> Module
>> Notification::Builder.ancestors
=> [ActiveRecord::Associations::Builder]
Is this expected behavior?
Yes
OK, so... what choices do you have?
You can use a different term than Builder. Like Factory. or Notification::NotificationBuilder
More info:
* http://www.rubydoc.info/docs/rails/3.1.1/ActiveRecord/Associations/Builder/Association
* http://apidock.com/rails/ActiveRecord/Associations/Builder

This problem exists because you are using an ActiveRecord model as a namespace. I created a gist with some experimentation until I saw the root cause.
ActiveRecord models include the ActiveRecord::Associations module. Since you can get to a constant when including a module the Builder constant defined within Associations is now also reachable through the AR model. You will get this behavior with every class defined in the modules, which are included into an AR model:
1.9.3-p194 :010 > Post.ancestors
=> [Post(id: integer, title: string, published_at: datetime, created_at: datetime, updated_at: datetime), Post::GeneratedFeatureMethods, #<Module:0x007fec74dc33a0>, ActiveRecord::Base, ActiveRecord::Core, ActiveRecord::Store, ActiveRecord::Serialization, ActiveModel::Serializers::Xml, ActiveModel::Serializers::JSON, ActiveModel::Serialization, ActiveRecord::Reflection, ActiveRecord::Transactions, ActiveRecord::Aggregations, ActiveRecord::NestedAttributes, ActiveRecord::AutosaveAssociation, ActiveModel::SecurePassword, ActiveRecord::Associations, ActiveRecord::Timestamp, ActiveModel::Validations::Callbacks, ActiveRecord::Callbacks, ActiveRecord::AttributeMethods::Serialization, ActiveRecord::AttributeMethods::Dirty, ActiveModel::Dirty, ActiveRecord::AttributeMethods::TimeZoneConversion, ActiveRecord::AttributeMethods::PrimaryKey, ActiveRecord::AttributeMethods::Query, ActiveRecord::AttributeMethods::BeforeTypeCast, ActiveRecord::AttributeMethods::Write, ActiveRecord::AttributeMethods::Read, ActiveRecord::AttributeMethods, ActiveModel::AttributeMethods, ActiveRecord::Locking::Pessimistic, ActiveRecord::Locking::Optimistic, ActiveRecord::CounterCache, ActiveRecord::Validations, ActiveModel::Validations::HelperMethods, ActiveSupport::Callbacks, ActiveModel::Validations, ActiveRecord::Integration, ActiveModel::Conversion, ActiveRecord::AttributeAssignment, ActiveModel::ForbiddenAttributesProtection, ActiveModel::DeprecatedMassAssignmentSecurity, ActiveRecord::Sanitization, ActiveRecord::Scoping::Named, ActiveRecord::Scoping::Default, ActiveRecord::Scoping, ActiveRecord::Inheritance, ActiveRecord::ModelSchema, ActiveRecord::ReadonlyAttributes, ActiveRecord::Persistence, Object, PP::ObjectMixin, ActiveSupport::Dependencies::Loadable, V8::Conversion::Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]
A possible solution is to use a module as a namespace. For example module Notifications.

Related

Rails Digest::UUID v5 (vs) Postgresql uuid-ossp v5

I'm getting different V5 UUIDs when generating with Rails Digest::UUID and Postgresql uuid-ossp.
Rails:
[58] pry(main)> Digest::UUID.uuid_v5('e90bf6ab-f698-4faa-9d0f-810917dea53a', 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
=> "db68e7ad-332a-57a7-9638-a507f76ded93"
Postgresql uuid-ossp:
select uuid_generate_v5('e90bf6ab-f698-4faa-9d0f-810917dea53a', 'e90bf6ab-f698-4faa-9d0f-810917dea53a');
uuid_generate_v5
--------------------------------------
6c569b95-a6fe-5553-a6f5-cd871ab30178
What would be the reason? I thought both should generate the same UUID when the input is the same, but it is different!
It's not an answer to the question about why Rails produces a different result, but if you want to produce v5 UUID in your Ruby code, you could use uuidtools. It returns the same result as PSQL:
~ pry
[1] pry(main)> require 'uuidtools'
=> true
[2] pry(main)> UUIDTools::UUID.sha1_create(UUIDTools::UUID.parse('e90bf6ab-f698-4faa-9d0f-810917dea53a'), 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
=> #<UUID:0x3fe09ea60dd8 UUID:6c569b95-a6fe-5553-a6f5-cd871ab30178>
[3] pry(main)>
It seems that a patch is proposed so that working string-representation of namespaces can be enabled explicitly
The new behavior will be enabled by setting the config.active_support.use_rfc4122_namespaced_uuids option to
true.
but, the patch is very recent and it could be still under test. People can be afraid it breaks things. Check
https://github.com/rails/rails/issues/37681
https://github.com/rails/rails/pull/37682/files
Meanwhile, a workaround is to pack the namespace string
ns=n.scan(/(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})/).flatten.map { |s| s.to_i(16) }.pack("NnnnnN")
In your example
irb(main):037:0> n='e90bf6ab-f698-4faa-9d0f-810917dea53a'
=> "e90bf6ab-f698-4faa-9d0f-810917dea53a"
irb(main):038:0> ns=n.scan(/(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})/).flatten.map { |s| s.to_i(16) }.pack("NnnnnN")
=> "\xE9\v\xF6\xAB\xF6\x98O\xAA\x9D\x0F\x81\t\x17\xDE\xA5:"
irb(main):039:0> puts Digest::UUID.uuid_v5(ns, 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
6c569b95-a6fe-5553-a6f5-cd871ab30178

Method to list of attributes of a model which establishes a DB connection

In old Rails versions one could call a Model by name in the console and get a list of all attributes. In the new versions that is not possible - at least not right away until a connection is established:
$ rails c
Loading development environment (Rails 4.2.1)
>> Product
=> Product (call 'Product.connection' to establish a connection)
>>
I know that this is a feature and not a bug. And I know that a simple Product.count would establish the connection. I'm wondering what other method I could call to receive the list of attributes that automatically establishes the connection to the database. I'm looking for this output:
=> Product(id: integer, name: string, created_at: datetime, updated_at: datetime)
You can use column_names wich returns an Array of Model columns as string like :
$ rails c
Loading development environment (Rails 4.2.1)
>> Product.column_names
=> ["id", "name", "created_at", "updated_at"]
>>
or columns_hash which returns more informations
In your config/application.rb you can establish the connection like so:
console do
ActiveRecord::Base.connection
end

Rails: Can't assign model object values after migrating to 2.3.5

I'm migrating a Rails app from 2.0.2 to 2.3.5.
I have a model class:
class EventQuery < ActiveRecord::Base
set_table_name 'EventQueries'
def to_s; name; end
end
I'm debugging the problem in the console. I try to assign an EventQuery object an attribute:
./script/console production
Loading production environment (Rails 2.3.5)
>> q=EventQuery.new
=> #<EventQuery id: nil, role: nil, name: nil, description: nil, query: nil>
>> q.role="ADM"
> TypeError: can't convert String into Integer
from /usr/lib64/ruby/gems/1.8/gem /activerecord-2.3.5/lib/active_record/dirty.rb:135:in `[]='
(...)
In Rails 2.0.2 it works fine:
./script/console production
Loading production environment (Rails 2.0.2)
>> q=EventQuery.new
=> #< EventQuery id: nil, role: nil, name: nil, description: nil, query: nil
>> q.role='ADM'
=> "ADM"
Any ideas? Thank you!
I'm now thinking this must be related to plugin record_modified which is part of the application.
This problem is related to a name conflict with a method in the record_modified plugin. The console works fine if I exclude the plugin from loading.
I have decided to remove the record_modified plugin and refactor the code. Instead of using RecordModified methods I'm using ActiveRecord as follows:
RecordModified.modified? --> ActiveRecord.changed?
RecordModified.changed_columns --> ActiveRecord.changes + minor refactoring

:subclasses Replacement in Rails 3

Right now I have:
#models = ActiveRecord::Base.send(:subclasses)
But I get this in the log:
DEPRECATION WARNING: subclasses is deprecated and will be removed from Rails 3.0 (use descendants instead). (called from send at /Users/*******/m3p0/app/controllers/roles_controller.rb:50)
What should I replace :subclasses with?
It is in the warning, it says use descendants instead.
ActiveRecord::Base.send(:descendants)
Also, these methods are publicly available so you don't need to use the .send syntax, you can call it explicitly.
ActiveRecord::Base.descendants
Here is the output from one of my apps:
ruby-1.9.2-p180 :001 > ActiveRecord::Base.subclasses
=> [Achievement(id: integer...
ruby-1.9.2-p180 :003 > ActiveRecord::Base.descendants
=> [Achievement(id: integer...

Strange problem with activerecord fetch/find with column name 'changes' in a RAILS 2.3.8 model

How is this possible?
Loading development environment (Rails 2.3.8)
>> wq = Wq.first(:conditions =>['widget_id=? AND qs_id=?',1,1])
=> #<Wq id: 1, widget_id: 1, qs_id: 1, operator: 0, requirements: "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2", changes: "1", route: 2, created_at: "2010-09-07 08:11:05", updated_at: "2010-11-24 10:25:53", body: "Which specific area of gyt are you aiming to addres...", options: "['xyz','pqr']", input_type: nil, status: 1>
>> wq.changes
=> {}
>> wq.changes
=> {}
>> wq.requirements
=> "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2"
>> wq.changes
=> "1"
Why is wq.changes coming as null initially and then after logging wq.requirements, wq.changes seems to come fine?
All necessary fields that are being fetched are withing a attr_accessible in the model.
I am not able to understand this situation, please help all you rails gurus.
The attribute name 'changes' conflicts with the AR::Dirty functionality. You should probably pick a different name for that column.
Here's the rails3 api docs for Dirty:
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
In rails2 it's in ActiveRecord rather than ActiveModel.
If you aren't able to rename the column, you could work around the issue by calling #model_obj[:changes] instead.
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb#L1466

Resources