I have a Model "Tag" that links to the dbpedia article for a particular topic. I want to create a virtual attribute that will use this dbpedia URI to generate a wikipedia URI. Here's roughly what the class looks like:
class Tag < ActiveRecord::Base
# Validation
validates_presence_of :dbpedia_uri, :label
#Virtual Attribute
def wikipedia_uri
"#{dbpedia_uri}".gsub("dbpedia.org/resource", "wikipedia.org/wiki")
end
end
When I go into my console and try to check Tag.all[1].wikipedia_uri, I get the following error:
NoMethodError: undefined method `wikipedia_uri' for #<Tag:0xa0c79ac>
Additionally, I have a second Model (Map) with virtual attributes that do work, but when I change them, I still get the old value. So when the code is:
def thumbnail_uri
"#{map_base_uri}/thumbnails/#{identifier}.jpg"
end
Map.all[1].thumbnail_uri returns something like: "http://WEBSITE/maps/thumbnails/g3201b.ct002662.jpg"
and when I change the code to:
def thumbnail_uri
"test"
end
I still get the output: "http://WEBSITE/maps/thumbnails/g3201b.ct002662.jpg"
I've tried reloading my Rails Console, but it still doesn't seem to be updating the virtual attributes. Any thoughts on what could be causing this issue?
Figured out the issue. I was stupidly running my console in a directory for a duplicate backup set of files, so all of the methods and variables from the backup were available in the console, but my changes to the current code weren't affecting it
Related
I have a controller that should output suggestions for a form. The controller is under the Admin::Shop namespace and is located at app/controllers/admin/shop/suggest_controller.rb
class Admin::Shop::SuggestController < Admin::AdminsAreaController
def index
render:json => Shop::Product.all.group(:name).pluck(:name) and return
end
end
Shop::Product is a model defined under app/models/shop/product.rb as follows:
class Shop::Product < PostgresRecord
# ...
end
I can run queries on this model from the console and rspec tests without any issues. But when I try querying from within the controller, if the file changes (I edit something and save) I get uninitialized constant Admin::Shop::SuggestController::Shop.
To get it to work again I have to restart the server (using Puma).
I get the same behavior when trying to query from other controllers in other namespaces.
There's nothing relevant in the logs.
What am I doing wrong?
Try ::Shop::Product.all.group. That'll cause the constant lookup to begin in the global namespace.
There are a number of good articles written on the subject. Give them a read!
I need to handle a particular case of generating email views with URLs constructed from non-persisted data.
Example : assume my user can create posts, and that triggers a post creation notification email, I'd like to send the user an example of fake post creation. For this, I am using a FactoryGirl.build(:post) and passing this to my PostMailer.notify_of_creation(#post)
In everyday Rails life, we use the route url_helpers by passing as argument the model itself, and the route generator will automatically convert the model into its ID to be used for the route URL generation (in article_path(#article), the routes helper converts #article into #article.id for constructing the /articles/:id URL.
I believe it is the same in ActiveRecord, but anyways in Mongoid, this conversion fails if the model is not persisted (and this is somewhat nice as it prevents the generation of URLs that may not correspond to actual data)
So in my specific case, URL generation crashes as the model is not persisted:
<%= post_url(#post_not_persisted) %>
crashes with
ActionView::Template::Error: No route matches {:action=>"show", :controller=>"posts", :post_id=>#<Post _id: 59b3ea2aaba9cf202d4eecb6 ...
Is there a way I can bypass this limitation only in a very specific scope ? Otherwise I could replace all my resource_path(#model) by resource_path(#model.id.to_s) or better #model.class.name but this doesn't feel like the right situation...
EDIT :
The main problem is
Foo.new.to_param # => nil
# whereas
Foo.new.id.to_s # => "59b528e8aba9cf74ce5d06c0"
I need to force to_param to return the ID (or something else) even if the model is not persisted. Right now I'm looking at refinements to see if I can use a scoped monkeypatch but if you have better ideas please be my guest :-)
module ForceToParamToUseIdRefinement
refine Foo do
def to_param
self.class.name + 'ID'
end
end
end
However I seem to have a small scope problem when using my refinement, as this doesn't bubble up as expected to url_helpers. It works fine when using te refinement in the console though (Foo.new.to_param # => 59b528e8aba9cf74ce5d06c0)
I found a way using dynamic method override. I don't really like it but it gets the job done. I am basically monkeypatching the instances I use during my tests.
To make it easier, I have created a class method example_model_accessor that basically behaves like attr_accessor excepts that the setter patches the #to_param method of the object
def example_model_accessor(model_name)
attr_reader model_name
define_method(:"#{model_name}=") do |instance|
def instance.to_param
self.class.name + 'ID'
end
instance_variable_set(:"##{model_name}", instance)
end
end
Then in my code I can just use
class Testing
example_model_accessor :message
def generate_view_with_unpersisted_data
self.message = FactoryGirl.build(:message)
MessageMailer.created(message).deliver_now
end
end
# views/message_mailer/created.html.erb
...
<%= message_path(#message) %> <!-- Will work now and output "/messages/MessageID" ! -->
I have three models: Stockholder and Folder; each Stock has_many Stockholders, each Stockholder has_one :folder. The Folder record is created within the Stockholder model like so.
before_create :build_default_folder
private
def build_default_folder
logger.debug "The debug #{self.holder_index}"
build_folder(name: "#{self.holder_index}. #{self.holder_name}", company_id: self.stock.company.id, parent_id: self.stock.company.folders.find_by_name("#{self.stock.security_series} #{self.stock.security_class} Stocks").id)
true
end
Upon submission of the form in which information for stockholders are entered, I would like to redirect to the folder that was just created. And am attempting to do so with the following redirect:
#stockholder=Stockholder.find(params[:id])
if #stockholder.update(stockholder_params)
redirect_to company_browse_path(#stockholder.stock.company, #stockholder.folder.id)
end
This, however, results in the following error:
Undefined method `id' for nil:NilClass
There is no issue, however, if the folder already exists and I'm simply editing the stockholder. This leads me to believe that folder is not created in time for the controller to be able to use it as a parameter in the redirect. How might I work around this?
I don't know exactly what you are trying to do, but it looks like there is some stuff missing. First, you need to associate the Folder with a Stockholder, the next thing is, your Folder needs to be saved. I don't know what build_folder does as I've never seen that before but here is what works for me:
before_create :build_default_folder
private
def build_default_folder
folder = Folder.new(stockholder_id: self.id, name: "a name")
folder.save
end
Figured it out, had to enter the redirect as follows:
redirect_to company_browse_path(#stockholder.stock.company, Folder.find_by(stockholder: #stockholder))
Using rails version 4.0.5 currently. I can't seem to find a canonical answer to whether this is the best (DRY, MVC, etc.) method to validate what seems to me to be a simple validation. In my model, I have a unique document_id. This document_id should appear in the filename (in this case, always xml files) e.g. blah/blah.document_id.xml and it should also appear as an attribute on the root element of the xml document e.g. id='document_id'. I'd like to write (assuming the document_id and filename don't match):
doc = Document.new
...
if !doc.valid?
puts doc.errors[:document_id] # <= 'document_id does not match filename'
end
Here's the closest I've gotten:
doc = Document.new
...
if !doc.valid?
... # no error caught here
end
if doc.document_id_matches_filename(xml_file)
puts 'document_id does not match filename'
end
Inside app/models/document.rb, I have this:
class Document < ActiveRecord::Base
...
def document_id_matches_filename(filename)
return self.document_id == File.basename(filename, '.xml')
end
end
I could add a filename column to the Document model and use doc.valid with a custom validator, but I don't want to effectively store the document_id twice. I can find information on sending parameters to custom validators or creating Validation classes, but those all seem to do things like use Time.now or things like that -- dynamic information but not specific information related to the document. I could try to find the xml file based on the doc id in a custom validator, but my rake task or Controller already has this information, it just needs to hand it off to the Model to validate against. Theoretically this would either be a rake task or on the Controller, I can't imagine that mattering.
Thanks in advance.
How about using a virtual attribute to store the filename so your model has access?
Something like...
class Document < ActiveRecord::Base
attr_accessor :filename
validate :document_id_matches_filename
private
def document_id_matches_filename
errors.add(:document_id, 'does not match filename') unless self.document_id == File.basename(self.filename, '.xml')
end
end
That allows you to use ActiveModel's validations and get the error message you're looking for and encapsulate it all within the model without having to add an extra DB column.
I get confused at Rails pluralising and camelcasing. Especially with my long but clear names.
I have a User model and an Account model. I also have a user_to_account_log_history model and controller for saving transfers between the two. The relationships are setup.
I ran...
$ rails generate controller UserToAccountLogHistories
...which created the following:
# app/controllers/user_to_account_log_histories_controller.rb
class UserToAccountLogHistoriesController < ApplicationController
# a simple index method
end
# app/models/user_to_account_log_history.rb
class UserToAccountLogHistory < ActiveRecord::Base
end
My routes seems to be in place (rake routes output, truncated):
user_usertoaccountloghistories GET /users/:user_id/usertoaccountloghistories(.:format) {:action=>"index", :controller=>"usertoaccountloghistories"}
But I'm getting an uninitialized constant UsertoaccountloghistoriesController. Why? I get the feeling it´s the long names camelcasing and pluralising that messes things up.
The problem is that you have a class called UserToAccountLogHistoriesController but no class called UsertoaccountloghistoriesController -- note the difference in capitalisation here.
It's not clear from your question exactly how you've defined your route, but I suspect that you've got a route referring to usertoaccountloghistories when actually you want to refer to user_to_account_log_histories.
In the rails console, observe the following:
> "hello_world".camelize
# => "HelloWorld"
> "helloworld".camelize
# => "Helloworld"