ActiveResource toplevel constant issue; query path is different intermittently. - ruby-on-rails

Currently I have the above mentioned issue.
From my understanding, this is an on going issue with rails autoloading and how there are standards in namespacing the various class.
Product which retrieve product/products without any scope.
# product.rb
class Product < ActiveResource::Base
self.site = "#{end_point}/api/v2"
....
end
Market::Product which provide us an interface to seek product under the market scope, which is similar to a product.
# market/product.rb
class Market
class Product < ::Product
self.site = "#{end_point}/api/v2/markets/:market_name"
....
end
end
Controller could call the market product object, but the object being return is just product
# market_product_controller.rb
class MarketProductController < ApplicationController
def index
#object = ::Market::Product.all
end
....
end
On api, they are 2 different end-point, with 2 different result sets.
So far, when calling ::Market::Product, it seems like it is using ::Product url and :market_name as a params to that url.
Is there a good solution to this?
How did the rest of the community get around this issue?
Cheers for any help that is given.

Found the answer to my problem.
http://blog.revathskumar.com/2013/12/activeresource-passing-prefix-options.html
It would have seem that I have used activeresource incorrectly all these while.
self.site = end_point
self.prefix = '/api/v2/markets/:market_name/'
This would be the right way to use it when it comes to nested resource.
This solution would work nicely when it gets to ActiveResource::Base.rb:1029. It would be able to get the right prefix_parameters from the prefix_source and then create the right path to the remote end point.
Hope this solution would help others who might encounter the same issue in the future.

Related

Ruby: undefined method for not initialized

Note: There are numerous answers explaining that you can get this error when you subclass ActiveRecord::Base and add an #initialize without super. No answer explains what is actually happening.
I am working in someone else's code and I have an HTTParty service in a Rails app with the following class hierarchy. Note the subclass #initialize with a differing signature to the parent class.
module A
class Base
include HTTParty
...
end
end
module A
class User < Base
def initialize(user)
#user = user
end
end
end
module A
class PublicUser < User
def initialize(opts = {})
#limit = opts[:limit]
# no call to super
end
end
end
Locally there are no problems with this, but in SemaphoreCI the following results:
A::PublicUser.new(limit: 1).some_method
undefined method `some_method' for #<A::PublicUser not initialized>
I can't find any documentation about the "not initialized" message. What causes this sort of failure?
OK, I got it. I also tagged your question with ruby-on-rails, since plain good ruby would rare give such a weird behaviour.
You have experienced two different issues, more or less unrelated.
#<A::PublicUser not initialized> is a result of (sic!) calling inspect on A::PublicUser. So, ruby tries to format an error message and—voilà—the class is printed out that way.
Rails messes with you, as well as with constant lookup. A::Base name conflicts with ActiveRecord::Base, and guess what is resolved when class User < Base is met. To replicate this behaviour you might open a console and do: class Q < ActiveRecord::Base; end; Q.allocate, resulting in #<Q not initialized>. (Do you already love Rails as I do?)
To fix this, either explicitly specify class User < A::Base or rename Base to MyBase. Sorry for suggesting that.

How to reassign STI class to a variable within model's method?

I've got STI like this:
class Post
end
class Post::Confirmed < Post
end
class Post::Draft < Post
def confirm!
becomes Post::Confirmed
end
end
...# somewhere in controller
# POST /posts/1/confirm
# POST /posts/1/confirm.json
def confirm
#post = Post::Draft.first
#post = #post.confirm! # this is the only way I can reload #post with Post::Confrmed
end
Is it somehow possible to make:
#post.confirm! # I want this #post(Post::Draft) to become Post::Confirmed without reassigning
Or is it just nor RoR way?
Thanks in advance!
The pattern I've found that works best here is having a datetime type field that records when the record was flagged.
For example:
def confirm!
self.confirmed_at = DateTime.now
self.save!
end
Then you can tell when something was confirmed. This comes in especially handy for when you have a situation where something will be flagged but isn't yet, such as setting a publishing date in the future.
Although it might seem a little annoying to not have your STI bag of tricks available, STI is not always the appropriate tool. Generally STI is to differentiate between similar but different models that have a lot of commonality or are used in a common context. It's not supposed to be used to handle different states of a singular model.
What you want in that case is a state-machine type pattern.

Controller 'new' method error

I am following a tutorial
and I am getting an error for my new action. I am using the tutorial as a guideline and my database differs. Here is my code:
def new
#skill = Skill.new
#skills = Skill.find(:all)
end
and here is my error message:
uninitialized constant SkillsController::Skill
#skill = Skill.new is the highlighted line so my mistake should be there somewhere. Thank you and I will keep trying to fix it with the power of research!
^^^^^^^^^^^^^^^solved^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Now I am getting a new error and it seems my database is not quite built right for the skill id's. I am trying to go to localhost:3000/skills/list, Here is my code:
class SkillsController < ApplicationController
def list
#skills = Skill.find(:all)
end
def show
#skills = Skill.find(params[:id])
end
and my error is:
Couldn't find Skill without an ID
and it highlights:
#skills = Skill.find(params[:id])
as the problem area. Thank you for anyone who knows how to solve this issue and also, If I should re-post as a different question let me know and I will do so. Thanks again and huzzah for the web dev community!
Syntax
Firstly, Rails 4 syntax should read as:
def new
#skills = Skill.all
end
--
Error
In regards to your error, as described in the comments, you really need to ensure you have the Skill class available in your application.
You must remember Rails is basically just a series of classes & modules, which means that if you're trying to call a "model", you're essentially calling a Ruby Class which needs to be loaded (as constants).
The problem is your application hasn't got the Skill class (model) loaded. This is most likely the result of not having it in your app/models directory:
#app/models/skill.rb
Class Skill < ActiveRecord::Base
...
end
What concerns me, though, is how your error is trying to wrap this model in another class - SkillsController. This would typically be the case if you've namespaced the controller; but either way you should create the model, restart your server & test again
I think you are messing up with rails conventions. Rails expect Controller class names to be pluralized, such that SkillsController would be the controller class for the skills table. Rails will then look for the class definition in a file called skills_controller.rb in the /app/controllers directory. For more information on naming conventions refer here.
Also since your error says uninitialized constant SkillsController::Skill. I think you don't have your skills table setup with Skill.rb model notice your table name will be plural and your model name will be singular

How to avoid race conditions when setting class variables on each request in Ruby on Rails

I have an active resource like model that communicates with a restful resource.
The resource path has some dynamic parameters so I'm setting some class variables on the model before each request.
I have something like this:
class MyClass << MySuperClass::Base
class << self
attr_accessor :site
attr_accessor :shop_id
attr_accessor :product_id
def get
RestClient.get(self.site)
end
def set_site(shop_id, product_id)
self.site = "http://example.com/api/shop/#{shop_id}/product/#{product_id}
end
end
end
In my application controller I have a before filter that sets the shop_id and product_id
class ApplicationController < ActionController::Base
before_filter :set_site
private
def set_site
MyClass.set_site(current_shop.id, current_product.id)
end
end
As I understand from here: http://m.onkey.org/thread-safety-for-your-rails
This could be the cause of some race conditions.
That article was written 3 years ago so it is still the case that setting class variables per request could cause a race condition?
And if so then what is the current best practice to achieve similar behavior without incurring a race condition?
The answer to this question says, "a standard rails app is single threaded." Check it for details.
Don't set class variables for classes which will have different values for different requests.
Rails will persist class variables between requests and you won't be able to guarantee that data won't leak between two different users' sessions.

How to always set a value for account-scope in Rails?

I'm working on a multi-user, multi-account App where 1 account can have n users. It is very important that every user can only access info from its account. My approach is to add an account_id to every model in the DB and than add a filter in every controller to only select objects with the current account_id. I will use the authorization plugin.
Is this approach a good idea?
What is the best way to always set the account_id for every object that is created without writing
object.account = #current_account
in every CREATE action? Maybe a filter?
Also I'm not sure about the best way to implement the filter for the select options. I need something like a general condition: No matter what else appears in the SQL statement, there is always a "WHERE account_id = XY".
Thanks for your help!
This is similar to a User.has_many :emails scenario. You don't want the user to see other peoples emails by changing the ID in the URL, so you do this:
#emails = current_user.emails
In your case, you can probably do something like this:
class ApplicationController < ActionController::Base
def current_account
#current_account ||= current_user && current_user.account
end
end
# In an imagined ProjectsController
#projects = current_account.projects
#project = current_account.projects.find(params[:id])
I know, I know, if you access Session-variables or Instance variables in your Model you didn't understand the MVC pattern and "should go back to PHP". But still, this could be very useful if you have - like us - a lot of controllers and actions where you don't always want to write #current_account.object.do_something (not very DRY).
The solution I found is very easy:
Step 1:
Add your current_account to Thread.current, so for example
class ApplicationController < ActionController::Base
before_filter :get_current_account
protected
def get_current_account
# somehow get the current account, depends on your approach
Thread.current[:account] = #account
end
end
Step 2:
Add a current_account method to all your models
#/lib/ar_current_account.rb
ActiveRecord::Base.class_eval do
def self.current_account
Thread.current[:account]
end
end
Step 3: Voilá, in your Models you can do something like this:
class MyModel < ActiveRecord::Base
belongs_to :account
# Set the default values
def initialize(params = nil)
super
self.account_id ||= current_account.id
end
end
You could also work with something like the before_validation callback in active_record and then make with a validation sure the account is always set.
The same approach could be used if you always want to add the current_user to every created object.
What do you think?
To answer your second question, check out the new default_scope feature in Rails 2.3.
I understand that you don't want to bother about scoping you account all time. Lets be honest, it's a pain in the a**.
To add a bit magic and have this scoping done seamlessly give a look at the following gem
http://gemcutter.org/gems/account_scopper
Hope this helps,
--
Sebastien Grosjean - ZenCocoon

Resources