hi i got existing project within a model (let call at a) that call a model (let call at b) i need from the the last model a.k.a get the ip of the client the issue is i kinda bound with restrictions.
the way the model beed add if from include mean it not for reall passed from controller
one of the restrictions is make the few changes as possible
there for my question is simple let say i need get the ip not from the controller but from the model and it not save anywhere so what way i can get the ip.
p.s i did try request.remote_ip but it don't know request
if possible can u show me link or example code so i will understand how to do.
You can do the following for getting the remote ip of the client, this is using a controller.
class TestsController < ApplicationController
def get_ip
ip = request.remote_ip
Test.use_ip(ip)
end
end
Assuming you have a model. I'm assuming it as Test
class Test < ActiveRecord::Base
def self.use_ip(ip)
puts ip
end
end
As per your requirement, which is going against convention of Rails (which is not a very good practice)
define the following in application_controller.rb
before_filter :pass_request_around
def pass_request_around
Thread.current[:request] = request
end
In model, request object should be available now
def get_ip
request = Thread.current[:request]
ip = request.remote_ip
end
Related
I am attempting to do the following based on ActionDispatch::RemoteIp::GetIp#calculate_ip
>> ActionDispatch::RemoteIp::GetIp.calculate_ip
However, I see this error:
!! #<NoMethodError: undefined method `calculate_ip' for ActionDispatch::RemoteIp::GetIp:Class>
So the question is, if this approach is flawed, how do I get the remote IP in the model?
Converting my comment into an answer:
An IP address only makes sense within the context of an http request. Therefore, you have to determine the IP address in the controller, not in the model. What will be the IP address when you use the model in a test or background job?
Inside a controller, you can get the IP address of the client with:
request.remote_ip
Now you can assign the value to a model attribute:
#object.ip_address = request.remote_ip
#object.save
The IP address can best be determined in the context of an HTTP request, in fact Rails does that for you and provides the result in request.remote_ip in any instantiated controller.
class SomeController < ApplicationController
def some_action
remote_ip = request.remote_ip
# You can now pass the ip anywhere you'd like to use it
end
end
My application controller looks like:
class ApplicationController
before_action :set_customer
def customer?
#customer.nil?
end
private
def set_customer
if request.host != "example.com"
#customer = Customer.find_by_domain("...")
else
if request.subdomain != "www"
#customer = Customer.find_by_sub("...")
end
end
end
end
How can I make tests for the ApplicationController?
I want to pass in URLS and then check if #customer is nil etc.
e.g URLS to test
example.com
www.example.com
google.com
hello.example.com
You shouldn't test instance variables. Move the code to a service object if needed.
assigns is going to be deprecated in Rails 5, so will be probably removed at some point. In controller tests, you want to check that the response is what you expect (response code) based on your user (authentication) and authorization, and possibly that the correct format is used.
In a different test (a "view" test, I mean the integration tests) you check also that the output in the view contains some value based on your instance variable.
In this case you can simply test that the service calls Customer.find_by_sub (or by_domain) based on your clause, and that's all you need to test, instance variable shouldn't be something you care about.
When using the Rails method, find_or_create_by, does it belong in the model or the controller? I am having a hard time understanding how to actually implement this method.
I want my rails application to accept JSON messages from users. The users will be sending data back to the server so it can be saved in the database. That being said, I would assume the user would have to use the 'POST' or 'PATCH method to store or update the data on my server. When I look at my routes the 'POST' method is used by the create action.
I have read the following Rails documentation but it didn't clarify anything to me. http://guides.rubyonrails.org/active_record_querying.html#find-or-create-by
Would I place the find_or_create_by method in my create action like so? Or does it belong somewhere else? It doesn't seem right in the create action...
class WifiNetworksController < ApplicationController
def create
#wifi_network = WifiNetwork.find_or_create_by(bssid: params[:bssid],
ssid: params[:ssid],
channel: params[:channel], etc...)
end
end
Ultimately I want:
Users to save new networks via JSON if it doesn't exist
Users to update existing networks via JSON if certain attributes have improved (like signal strength)
Thank you for your time!
Final Update - Thanks for the great advice everyone! I had to take a bit of everybody's advice to get it to work! Below is what I ended up doing.. Seems to work well with no errors.
def create
respond_to do |format|
if #wifi_network = WifiNetwork.find_by(bssid: wifi_network_params[:bssid])
# Logic for checking whether to update the record or not
#wifi_network.update_attributes(wifi_network_params) if #wifi_network.rssi < params[:rssi]
format.json { render :nothing => true }
else
# Must be a new wifi network, create it
#wifi_network = WifiNetwork.create(wifi_network_params)
format.json { render :nothing => true }
end
end
end
If you use strong params you can do this in your controller:
def create
#wifi_network = WifiNetwork.find_or_create_by(bssid: wifi_network_params[:bssid])
#wifi_network.update_attributes(wifi_network_params)
end
Then when a user makes a curl like:
curl -X POST localhost:3000/wifi_networks -d "wifi_network[bssid]=bssid1&wifi_network[ssid]=ssid1&wifi_network[channel]=channel1"
your create action will look up a WifiNetwork by it's bssid and update the ssid and channel attributes, or if it doesn't exist it will create a WifiNetwork with the bssid param and then update the newly created record with the rest of the atts. Be careful because if the wifi_network_params for the other attrs are empty they will update the params to nil.
I think it may be good to take a step back and really think about the interface of your application. Is there any particular reason why you need to use find_or_create_by and do everything in one controller action?
Why not simplify things and adhere to REST by having separate 'create' and 'update' actions on your WifiNetworksController:
class WifiNetworksController < ApplicationController
def create
#wifi_network = WifiNetwork.new(wifi_network_params)
if #wifi_network.save
# success response
else
# failure response
end
end
def update
# params[:id] won't work here if the client sending the request doesn't know the id of the
# wifi network, so replace it with the attribute you expect to be able to
# uniquely identify a WifiNetwork with.
if #wifi_network = WifiNetwork.find(params[:id])
# Logic for deciding whether to update or not
#wifi_network.update_attributes(wifi_network_params) if #wifi_network.signal_strength < params[:signal_strength]
else
# wifi_network not found, respond accordingly
end
end
private
# strong_parameters for Rails 4
def wifi_network_params
params.require(:wifi_network).permit(:ssid, :channel,...)
end
end
You could then have validations on your WifiNetwork model to ensure that certain attributes are unique, in order to avoid duplicates.
Or, if you really wanted to, you could combine both create and update into a single action, but create probably isn't the best name semantically.
EDIT: After your comment gave some background info, there probably isn't any benefit to using find_or_create_by, since you won't be able to tell if the record returned was 'created' or 'retrieved', which would allow you to avoid redundant update operations on it.
Assuming the bssid attribute is always a unique parameter:
def create
if #wifi_network = WifiNetwork.find(params[:bssid])
# Logic for checking whether to update the record or not
#wifi_network.update_attributes(wifi_network_params) if #wifi_network.signal_strength < params[:signal_strength]
else
# Must be a new wifi network, create it
#wifi_network = WifiNetwork.create(wifi_network_params)
end
end
Let's say you have simple model called Project and you need to store it's short url for later usage, when is the best way to compute it?
The best solution I have for now is a after_create hook, but that leads to code like
short_url || Rails.application.routes.url_helpers.project_path(project, host: HOSTNAME)
It does not feel right to access the url from the model.
In short, where do you put the code to compute the short_url?
Thank you,
I would add this code in the controller.
class ProjectsController < ApplicationController
def create
#project = Project.new(params[:project])
respond_to do |format|
if #project.save
#project.update_attribute(:short_url, project_url(#project))
(..)
end
end
It feels like this code belongs to the controller as it deals with http/url. Storing in the db sounds ok, but asking for the url is the controller's responsibility.
The line:
#project.update_attribute(:short_url, project_url(#project))
needs to be added below the call to .save (or .create), as only then the project_url helper can be called (the project object got its id already).
Is there a reason why you dont want to save the exact short_url instead? So when you need the url for the project object, you can just check if the short_url is present or not. I believe you can just add a decorator to determine the url of project.
#using Draper
class ProjectDecorator < Draper::Decorator
def effective_url
source.short_url.present? ? source.short_url : project_path(source)
# or source.short_url || project_path(source) if short_url will not be an empty string
# or source.short_url || source
end
end
I'm trying to add a string to the user model under a location column, based on the user's location. I have everything setup to the point that I know the value of #city+#state is added to the appropriate column in the correct model. The problem is, it appears that request.location.city and request.location.state function properly in the controller and views, but not in the model.
def add_location
#city = request.location.city
#state = request.location.state
#location = #city+#state
self.location = #location
end
When a user is created, rather than creating a string such as "losangelescalifornia", nothing is created. When I define #city = "bob" and #state = "cat", all users created have "bobcat" in the appropriate place. I know then that everything is functioning except these geolocation based methods. So my question is, how would I get these methods (correct me please if that is not what they are) to function in the model, being request.location.city and request.location.state? Many thanks in advance :)
I agree with Rudi's approach, mostly, but I'll offer a little more explanation. The concept you're wrestling with is MVC architecture, which is about separating responsibilities. The models should handle interaction with the DB (or other backend) without needing any knowledge of the context they're being used in (whether it be a an HTTP request or otherwise), views should not need to know about the backend, and controllers handle interactions between the two.
So in the case of your Rails app, the views and controllers have access to the request object, while your models do not. If you want to pass information from the current request to your model, it's up to your controller to do so. I would define your add_location as follows:
class User < ActiveRecord::Base
def add_location(city, state)
self.location = city.to_s + state.to_s # to_s just in case you got nils
end
end
And then in your controller:
class UsersController < ApplicationController
def create # I'm assuming it's create you're dealing with
...
#user.add_location(request.location.city, request.location.state)
...
end
end
I prefer not to pass the request object directly, because that really maintains the separation of the model from the current request. The User model doesn't need to know about request objects or how they work. All it knows is it's getting a city and a state.
Hope that helps.
request variable is not available in the model since it depends on the current HTTP request.
You have to pass to model as param.
def your_action
...
#model.add_location(request)
...
end
def add_location(request)
..
end