I'm creating a rails 5 application that I would like to be able to be accessed by different URLS. For example, the following two URLS would be powered by the same app;
example.com.au
example.co.nz
Let's say on the homepage of each I wanted to display a list of links to that countries main cities. Each main city will also have it's own page;
example.com.au/sydney
example.co.nz/auckland
They would share the same database.
This seems like it would be a common problem but I've been unable to find a straightforward answer.
Any help is much appreciated.
I'd "register" the URLs in a hash somewhere in your app:
{
"example.com.au" => "AU",
"example.co.nz" => "NZ",
}
Then you can add a method in your controller that looks something like this, to get the country:
def current_country
domain_name = extract_domain_name(request.original_url)
Settings.domain_country_map[domain_name]
end
If you're using a database to save the countries that have_many cities, you'd do a find in that method. And, for the home page, you can show current_country.cities.popular.limit(5) or something like that.
I also assume you wouldn't want example.co.nz/sydney to work. In that case, make sure that you scope it to the country: when the /city_name url is hit, you do a current_country.cities.find_by(slug: params[:city_slug]).
To make the route /city_slug work, you'd need something like this in your routes file:
get '/:city_slug', to: 'cities#show'
There are probably better ways of doing it, but if you call request.url, rails will return the current address. For example we could make the following in our application controller:
expose(:sydney?)
expose(:auckland?)
def sydney?
request.url.include?('sydney')
end
def auckland?
request.url.include?('auckland')
end
Then in your view:
<% if sydney? %>
<%= Do some stuff %>
<% elsif auckland? %>
<%= Do some other stuff %>
<% else %>
<%= Do stuff for no city %>
<% end %>
Remember to expose both methods in your controller or you can't access them from your view.
Related
I am trying to implement a search field in Rails 4 to find data behind two models. I have two controllers: buildings and rooms, and two models: building and room. A building, naturally, has many rooms and a room belongs to a building, which I have stated in the respective models.
Basically, a user could type either a building or a room name into the search form and it would return a response with details about the building or room. Of course, a building needs to have an address, and a room needs to know in which building it is with the address as well. So I'd need to display different details according to the searched for instance. Both have the same String attribute name, so this could make things easier.
I have no luck in finding a relevant example on how to implement such a search form. This is the working basic search I have at the moment in views\buildings\index.html.erb, which can only search buildings:
<%= form_tag({controller: "buildings", action: "show"}, method: "get", class: "nifty_form") do %>
<%= label_tag(:name, "Search for a building (later also rooms):") %>
<%= text_field_tag(:name) %>
<%= submit_tag("Search") %>
<% end %>
This is the show method in controllers\buildings_controller.rb:
def show
#building = Building.where('lower(name) = ?', params[:name].downcase).first
end
And this is the route it refers to:
get 'buildings/:id' => 'buildings#show'
Any and all help is appreciated!
I would recommend you to add the search code in the index method of buildings controller, there are many things you can do but here is what i will recommend:
def index
#
if params.has_key?(:name)
#buildings = Building.joins(:rooms).
where([
'lower(buildings.name) like ? or lower(rooms.name) like ?',
"%#{params[:name].downcase}%",
"%#{params[:name].downcase}%"
])
else
# your normal code goes here.
end
end
Any other information you need such address ( is that a different model ) can he included in there.
Hope this helps,
PD: if necessary you can render a different view when a search is present inside your if block
render action: 'my_custom_view', status: :ok
Due to time restrictions, I decided to merge these models into one common model, Space. It holds all the information that a building and a room needs. Thanks anyways!
I'm pretty new to rails and I'd like to set my links for a certain page dynamically. I have a table called "Unfinished" and it has a column called "link" (corrected from "links") I'd like to be able to call the "link" record in the view to set my link_to link path.
I am trying to do this...
<%= link_to #unfinished.link(:p => #post.id) do %> FINISH <% end %>
...but that's not working.
my controller says:
def show
#post = Post.find(params[:id])
#unfinished = Unfinished.where('progress = ?', #post.progress).last
end
and the controller logic works fine...until I try to put the #unfinished.link into link_to
Edit:
Error Message:
wrong number of arguments (1 for 0)
Model
class Unfinished < ActiveRecord::Base
end
The type of links are :
step1_path
step2_path
step3_path
I am making a multipage form that you can save partway through. Based on a value in the #post.progress column (like 1, 2, 3) the correct path to complete the post will be provided (step1_path, step2_path etc...)
try this.
<%= link_to eval(#unfinished.link.to_s) do %> FINISH <% end %>
since the link you want is actually a named route, so you will need to eval it.
but with this you wouldn't be able to be able to pass in the post id, which you will need.
If the route is the same for all records (save for what part you are on based on the progress attribute) do you even need to store it in the database? You could just make the link method return the path (that you would still need to eval).
something like
def link (post)
"step#{self.progress}_path(post.id)"
end
and then eval the link on the way back. but Not sure if that will work, just thinking out loud...
There are gems that do multi-stage forms perhaps looking into them might help?
I'm making an extremely simple CMS with rails that simply changes existing text and images on existing pages, nothing else. No changes to layout or anything crazy like that.
It's a bit like wordpress. Here's how it'll work:
If an admin is logged in, they have a special bar at the top of the
page that will enable them to edit the page.
Upon clicking, they go to the control panel, and all of the editable regions of viewable.
They can then edit the pages in the control panel, and save it.
What would be a good way to organize this flow?
I've made a start, and one thing that gets me is that all of the logic that populates a page' editable regions is occurring in the view:
splash/welcome.html.erb
<% #page = Page.find(name: 'welcome') %>
<% regions = #page.text_regions %>
<h1> <%= regions.where(name: 'title').text %> </h1> This returns the title (Welcome to my website)
<%= regions.where(name: 'welcometext').text %> This returns welcome text (This is my website bla bla)
I works fine, although the database has to be initially seeded with empty regions.
However, I don't like how there is logic in the view, so how could I populate a #regions instance variable in the application controller? Or is what I'm doing fine?
Maybe in the future I want users to be able to create pages, meaning I won't be able to have this logic in the view.
DRY
Looks like you'd be better using action_name (perhaps with a helper)
Without knowing the structure of what you're trying to do, this is just speculation, but you may be better doing something like this:
#app/models/region.rb
Class Region < ActiveRecord::Base
scope :title, -> { find_by name: "title" }
scope :welcome_text, -> { find_by name: "welcometext" }
end
#app/controllers/application_controller.rb
Class ApplicationController < ActionController::Base
before_action :set_page
private
def set_page
#page = Page.find_by name: action_name
#regions = #page.text_regions
end
end
#app/views/shared/_regions.html.erb
<h1><%= #regions.title.text %></h1>
<%= #regions.welcome_text %>
#app/views/static/welcome.html.erb
<%= render "shared/regions" %>
Hopefully this will give you some ideas as to how to achieve what you want?
I'm trying to hide parts of my views depending on the User role.
So let's say I want only admins to be able to destroy Products. Besides the code in the controller for preventing regular users from destroying records, I would do the following in the view:
<% if current_user.admin? %>
<%= link_to 'Delete', product, method: :delete %>
<% end %>
The previous code works, but it's prone to errors of omission, which may cause regular users to see links to actions they are not allowed to execute.
Also, if I decide later on that a new role (e.g. "moderator") can delete Products, I would have to find the views that display a delete link and add the logic allowing moderators to see it.
And if there are many models that can be deleted only by admin users (e.g. Promotion, User) maitenance of all the ifs would be pretty challenging.
Is there a better way of doing it? Maybe using helpers, or something similar? I'm looking for something maybe like this:
<%= destroy_link 'Delete', product %> # Only admins can see it
<%= edit_link 'Edit', promotion %> # Again, only admins see this link
<%= show_link 'Show', comment %> # Everyone sees this one
I found these two questions that are similar to mine, but none of them answered my question:
Show and hide based on user role in rails
Ruby on Rails (3) hiding parts of the view
I strongly recommend pundit.
It allows you to create "policies" for each model. For your Product model you might have a ProductPolicy that looks something like this
class ProductPolicy < ApplicationPolicy
def delete?
user.admin?
end
end
In your view you can do something like this
<% if policy(#post).delete? %>
<%= link_to 'Delete', product, method: :delete %>
<% end %>
If later on you want to add a moderator role, just modify the policy method
class ProductPolicy < ApplicationPolicy
def delete?
user.admin? || user.moderator?
end
end
So I kind of figured a way to move the IFs out of the view. First, I override the link_to helper in my application_helper.rb:
def link_to(text, path, options={})
super(text, path, options) unless options[:admin] and !current_user.admin?
end
Then on my views I use it as:
<%= link_to 'Edit Product', product, admin: true, ... %>
This prevents regular users from seeing admin links, but for other html tags with content inside, such as divs, tables etc., an if would still be needed.
CanCan is another gem that lets you define "Abilities" per user role.
In views you can use something like if can? :delete, #post to check if the
user may delete that specific post.
Using the CanCan and Role gems, what is still needed is a way to Check The Route and see if "current_user" has permissions to access that Route based on their role(s) - then show/hide based on that.
This saves the user clicking on things and getting told they cannot see it - or us having to write per-item "if" logic specifying what roles can see what list-items (which the customer will change periodically, as roles are changed/refined) around every single link in one's menu (consider a bootstrap menu with 50+ items nested in groups with html formatting, etc), which is insane.
If we must put if-logic around each menu-item, let's use the exact same logic for every item by checking the role/permissions we already defined in the Ability file.
But in our menu-list, we have route-helpers - not "controller/method" info, so how to test the user's ability to hit the controller-action specified for the "path" in each link?
To get the controller and method (action) of a path (my examples use the 'users_path' route-helper) ...
Rails.application.routes.recognize_path(app.users_path)
=> {:controller=>"users", :action=>"index"}
Get just the controller-name
Rails.application.routes.recognize_path(app.users_path)[:controller]
=> "users"
Ability uses the Model for its breakdown, so convert from controller name to it's model (assuming default naming used) ...
Rails.application.routes.recognize_path(app.users_path)[:controller].classify
=> "User"
Get just the action-name
Rails.application.routes.recognize_path(app.users_path)[:action]
=> "index"
And since the "can?" method needs a Symbol for the action, and Constant for the model, for each menu-item we get this:
path_hash = Rails.application.routes.recognize_path(app.users_path)
model = path_hash[:controller].classify.constantize
action = path_hash[:action].to_sym
Then use our existing Abilty system to check if the current_user can access it, we have to pass the action as a symbol and the Model as a constant, so ...
<% if can? action model %>
<%= link_to "Users List", users_path %>
<% end %>
Now we can change who can see this resource and link from the Ability file, without ever messing with the menu, again. But to make this a bit cleaner, I extracted out the lookup for each menu-item with this in the app-controller:
def get_path_parts(path)
path_hash = Rails.application.routes.recognize_path(path)
model_name = path_hash[:controller].classify.constantize
action_name = path_hash[:action].to_sym
return [model_name, action_name]
end
helper_method :get_path_parts
... so I could do this in the view (I took out all the html-formatting from the links for simplicity, here):
<% path_parts = get_path_parts(users_path); if can?(path_parts[1], path_parts[0]) %>
<%= link_to "Users Listing", users_path %>
<% end %>
... and to make this not take all day typing these per-menu-item if-wraps, I used regex find/replace with capture and wildcards to wrap this around every list-item in the menu-item listing in one pass.
It's far from ideal, and I could do a lot more to make it much better, but I don't have spare-time to write the rest of this missing-piece of the Role/CanCan system. I hope this part helps someone out.
I'm creating an application that tracks users and achievements (think, xbox live, etc.) These tables are linked via a join table. I would like to have a search form on my index that lets users type in a users name and a new page is loaded with a list of all achievements that user has earned. I'm not entirely sure how to set up this search form, on the index, to actually search the user table and return the results on a new page. Any help would be greatly appreciated. If you require more information then I'll be happy to provide it.
Here's a bit of skeleton code to get you started based off what I think you need from what you have said. I hope this is useful.
For the search bit you could do something like this in your index view:
<%= form_for User.new, :url => "search" do |f| %>
<%= f.label :name %>
<%- f.text_field :name %>
<%- end %>
In your controller:
def search
q = params[:user][:name]
#users = User.find(:all, :conditions => ["name LIKE %?%",q])
end
and in your search view:
<%-#users.each do |user| %>
Name: <%=user.name %>
<%- user.achievements.each do |achievement| %>
<%= achievement.name %>
<%- end %>
<%- end %>
You would, of course, need to ensure the users and achievement models are correctly linked:
class User << ActiveRecord::Base
has_many :achievements
end
There are plenty of tutorials and things about this e.g.:
http://blog.devinterface.com/2010/05/how-to-model-a-custom-search-form-in-rails/
Look the thing is every basic explanation in Rails3 starting with the Initial Tutorial provided by them explains you how to setup a new Controller/Model. The example was only one of thousands explaining the same problem.
It is a very broad range of different things you can do to achieve this. Basically you have to put some code in the controller:
which handles the search (including the activerecord stuff or whichever technique you use to access your model)
which sets some variables necessary for the search form
Setup two routes etc... Its to broad and completely covered even by the basic official rails3 tutorial.
Here is an application based on searchlogic is very useful and you can search by whatever you want
https://github.com/railscasts/176-searchlogic
You may want to check out the Ransack gem. https://github.com/activerecord-hackery/ransack