Complicated Website Structure - Rails - ruby-on-rails

I what I consider to be an advanced beginner with Ruby on Rails (and web design in general). I have this project and I'm having trouble wrapping my head around how to implement a recent development.
Initially the project was a simple blog for businesses to post articles that promote their company. It was meant for businesses in a specific town only and I had no trouble setting that up. Now we're trying to broaden out and let the site cater to many towns.
We want to have a home page where you select a town. That takes you to a list of blog posts from businesses in that town. You can navigate to a directory, contact us form, profile page etc. My problem is, I'd like each town to act like mini-site, so the directory only shows business associated with the town and the header image changes to reflect which town you've chosen.
My initial approach to this problem was to set up a towns scaffold and put in associations between towns, users and posts. The problem is 'storing' the chosen town somehow so that I can, for example, show only businesses for a specific town in the directory.
At the moment I have, in the posts model
belongs_to :user
In the user model
has_many :posts
has_many :towns
I have an action in towns controller to handle the directory which only has the line:
#users = User.order('trading_name ASC').where(:town => #town.id.to_s)
and then I want to display those users in the directory view.
I also have the following in my towns controller
before_action :set_town, only: [:show, :edit, :update, :destroy, :directory]
To be honest though, I can't fully wrap my head around the associations needed or even if this is the correct path to take. As it stands I get the following error when I try to access the directory view
Couldn't find Town without an ID
and it references the following method in towns controller (I'm using the friendly_id gem)
def set_town
#town = Town.friendly.find(params[:id])
end
Am I heading down the correct route with these associations (and missing something) or is there a better approach I could be taking. Also, I hope I've explained myself clearly. Please ask if there's something that's confusing.

I'm not sure exactly what your requirements are, but I'd say:
A user probably doesn't "have many" towns.
A user does "have many" posts.
A town does "have many" posts.
A post belongs to a town, and also a user.
Then, on your home page, you'd have a form with a drop-downlist of towns, and it should sumbit to an action like choose_town or something.
Your choose_town action should save the name or id of the town in a Cookie. You know about cookies? They basically allow you to save little bits of info between requests, for a specific user. They are stored in a users browser, but are sent back/forth from the server on every request. See http://guides.rubyonrails.org/action_controller_overview.html#cookies
Then, your set_town method, called every request, will look in the cookie to find the right town.
edit: #japed mentioned session, along with cookies. The session is identified by a cookie - however, information stored in the session is saved on the server, and is not sent back and forth between the browser and the server. In this case, either one is probably fine.

Related

Adding new functionality to Spreecommerse

I am planning to launch a website that acts as a sort of courier service where clients ask my company to find a certain product(for example an action figure) from another country that is not available in their country.
Spreecommerce(including some extensions) offers almost all the functionality that I require.
Shopping Cart, Payment system, CMS, Open Authentication, Commenting system.
One important piece of functionality remains, that is an Enquiry system. with this system customers can request certain products they want.
The form they will use will have the following information:
Name Description
Price(optional)
Barcode(optional)
Category(Using the same taxon system as products)
Quantity
Images
Comments (using the spree-contrib/spree_comments)
My idea is that the user will be presented will have a form which when submitted the admin can view. The admin can view/modify the enquiry and respond using the commenting system. Once the item is located and confirmed, the admin would then create a product and add it to the user’s shopping cart which the user can finalise using the normal spree order check out system.
So my modifications to the system would be:
- Add the Enquiry form for both client and administrator(administrator can place enquiries in the name of the customer)
- Make Products private to the users who ordered them
- Disable the “products” page for users as each product is unique to each user
I am at the point of creating the Enquiry form page. I looked at the spreecommerse documentation and there doesn’t seem to be instructions on adding a new page and integrating it into the system. The documentation focuses more on modifying current pages.
Therefore I decided to view a project that implemented somewhat similar functionality and emulate what it did.
I went with spree-contrib/spree_static_content. So what I did was:
Create an enquiry migration using spree_products tables as guidelines with the following information:
enquiries table
enquiries_taxons table(Used for “categories”)
enquiries_variants table(Used for “image uploads”)
Created the following views under app/views/spree/admin (These views are mostly simplified versions of the product views) :
enquiries/_form.html.erb
enquiries/edit.html.erb
enquiries/index.html.erb
enquiries/new.html.erb
shared/_enquiries_sidebar_menu.html.erb
shared/_enquiry_tabs.html.erb
To add the menu item in the admin section:
app/overrides/add_enquiries_to_admin_main_menu.rb
Created an empty EnquiriesController that inherits from Admin::BaseController
Created an Enquiries model that inherits from Spree::Base and is loosely based on the Products model.
Things that I am confused about so far are:
How is CRUD handled?
Where are the spree.admin_{name}_url values being stored?
If there are any tutorials on how to properly create such functionality, it would also be greatly appreciated.
Hmm, why not use the Spree::Product model as a starting point? Just add custom attributes to that model/table. I suggest this because building your own tables and relations to variants and taxons seems like reinventing the wheel.
You've got a good start here. What you are missing so far is routes
spree.admin_{name}_url values being stored?
...you don't necessarily need to use the spree. namespace for routes if your controllers inherit from Spree::StoreController or Spree::AdminController. The Spree base controllers provide a bunch of useful CRUD logic. In your case you'd need a routes.rb file that looked something like this:
Rails.application.routes.draw do
mount Spree::Core::Engine, :at => '/'
end
Spree::Core::Engine.add_routes do
#public enquiries, e.g. that inherit Spree::StoreController
get 'enquiries', :to => 'enquiries#index', :as => :enquiries
end
Spree::Core::Engine.add_routes do
namespace :admin do
resources :enquiries
end
end
Hottip: check out the spree_scaffold gem. It makes stubbing out models/controllers/view/routes hella quick

Do I generate multiple controllers?

I'm on the rails learning journey and am going about making my first rails app. It's a very simple app where users can create posts on a variety of topics.
I generated my first scaffold for a page I want to have called 'London' (rails generate scaffold london location:string content:text). Users of the site can post a post and location of a place to visit in London.
Then I wanted to replicate this functionality for 'Paris'. Do I generate a new scaffold or go about it a different way? Some advice would be appreciated.
Also the url gets pluralized (mywebsite.com/londons). I added
resources :londons, :path => "london"
which changed the url but when I go to make a post I get a No route matches [POST] "/londons" error. Anyone got a fix for this?
Thank you!
well instead of generating controllers for each city a better way could be to create relationships between models.For example you could create a cities and a locations scaffold then inside your city model you can do
has_many :locations
and inside you locations model you can do
belongs_to :city
that way you wouldn't need to create new scaffolds for every city.You can read up on how to use relationships from the guides here
Well probably You want to generalise things first :)
What You actually need are pages (or maybe topics, articles). You can implement Page model that will have such attributes as title (which can be London, Paris etc).
The You will introduce a PagesController. index action will lead to a list of pages, show will render particular page.
In your routes You will do something like this:
resources :pages

Which rails controller should I use?

In my app Users can Like Programs. Each of those is a model, Like is polymorphic.
At some point I will want to see all the Users that Like a Program or all Programs a User Likes.
Is it better to have a likes and likers controller action in the users and programs controller? Or should I have the likes controller as a nested resource with both a users and programs action (or an Index which checks which nested resource is being used)?
I realize all of these can work, but wasn't sure what was Rails best practices.
I would structure your app to have a UsersController with a likes member action which returns the Programs that user likes. And then have a ProgramsController with a likers member action, which gives the Users which like that program.
To simplify things further, you could also just include the user's likes in the show action, (and similarly, show the users who like a program in the program's show action), although you may end up fetching more information than is necessary in the show actions by doing it that way.
Restfully, you would have a LikesController and a 'create' action within it would take a user_id and a program_id. It's likely the user will be logged in (and won't be passed in the URL), and it will make sense to create a Like, passing a program_id to a url that looks something like this:
POST /likes, :params => { :program_id => ___ }
You may want to show a list of Likes (index page), perhaps allowing users to edit and delete. If this is the case, all your actions would be on the likes_controller. Usually, it depends on your situation, but a restful design is usually the right place to start.

Rails and REST confusion

I am confused about how to set up a RESTful rails app. I have some models- Courses, Students, Assignments, and StudentHandsInAssignment. There's a many to many relationship between Courses and Students, a one to many between Courses and Assignments, and two one to many's from Students and Assignments to StudentHandsInAssignment.
Now I am thinking about controllers and routing. From what I have read I want to be RESTful which means making resources. However, I can't quite figure out how to get all of the things I want in the resources supplied by rails.
In particular, I want to have an elaborate view of all the grades for the students in a course. Now, do I use the simple "GET /courses/:id" route and corresponding method in the controller or use something more specific like "GET /courses/:id/view_complete_grade_summary". I think I would like to do something like the latter because I might have multiple different ways of viewing a course. How does one handle this situation and set up that route/controller?
Also, I'll need to have a form to enter in grades in the app. This will create StudentHandsInAssignment objects and store them in the database. But, when I use "GET student_hands_in_assignments/new" to display a form (and eventually submit to call "create") how do I pass around the student id and the assignment id? Should it be something like this:
"GET student_hands_in_assignments/new/:student_id/:assignment_id"
or like this:
"GET student_hands_in_assignments/new/?student_id=1&assignment_id=2"
I guess I am confused about how to get the required info about the student and the assignment into a RESTful url. What is the best way to handle these situations?
There's no one "right" way to organize resources, IMO. My first thought for grading would be student-centric: student/:student_id/course/:course_id/grade, but it could just as easily be course-centric, or both.
I wouldn't have a separate page for entering grades, though, rather something hanging off a student page (list of enrolled courses with in-page, Ajax-centric grade entry) and off a course page (list of students with same grade entry scheme). Otherwise there are app transitions that just add cognitive overhead without any benefit.
For things like the course grade summary I'd just have /course/:course_id/grade_summary or whatever is necessary.
Using 'pretty' URLs is not necessary for a RESTful application, though I find that creating pretty URLs helps me organize my resources helps other maintain their sanity while maintaining my code.
I would consider using this URLs with the scenarios you describe:
# Renders the form to be submitted
/students/:student_id/assignments/:assignment_id
Add something like the following to your routes.rb:
resources :students do
resources :assignments
end
which will call:
{controller => 'assignments', :action => 'show'}
with the parameters:
{id => ':assignment_id', student_id => ':student_id'}
As a side note, consider changing the name of your model 'StudentHandsInAssignment' to something else. A student handing in an assignment is neither a Model nor a Controller, it's an action upon a resource. It may make more sense to have a AssignmentController with an action "hand_in" that updates the Assignment model (presumably with a status).
Hope this helps.

Prevent modification ("hacking") of hidden fields in form in rails3?

So lets say I have a form for submitting a new post.
The form has a hidden field which specify's the category_id. We are also on the show view for that very category.
What I'm worried about, is that someone using something like firebug, might just edit the category id in the code, and then submit the form - creating a post for a different category.
Obviously my form is more complicated and a different scenario - but the idea is the same. I also cannot define the category in the post's create controller, as the category will be different on each show view...
Any solutions?
EDIT:
Here is a better question - is it possible to grab the Category id in the create controller for the post, if its not in a hidden field?
Does your site have the concept of permissions / access control lists on the categories themselves? If the user would have access to the other category, then I'd say there's no worry here since there's nothing stopping them from going to that other category and doing the same.
If your categories are restricted in some manner, then I'd suggest nesting your Post under a category (nested resource routes) and do a before_filter to ensure you're granted access to the appropriate category.
config/routes.rb
resources :categories do
resources :posts
end
app/controllers/posts_controller
before_filter :ensure_category_access
def create
#post = #category.posts.new(params[:post])
...
end
private
def ensure_category_access
#category = Category.find(params[:category_id])
# do whatever you need to do. if you don't have to validate access, then I'm not sure I'd worry about this.
# If the user wants to change their category in their post instead of
# going to the other category and posting there, I don't think I see a concern?
end
URL would look like
GET
/categories/1/posts/new
POST
/categories/1/posts
pst is right- never trust the user. Double-check the value sent via the view in your controller and, if it does't match something valid, kick the user out (auto-logout) and send the admin an email. You may also want to lock the user's account if it keeps happening.
Never, ever trust the user, of course ;-)
Now, that being said, it is possible to with a very high degree of confidence rely on hidden fields for temporal storage/staging (although this can generally also be handled entirely on the server with the session as well): ASP.NET follows this model and it has proven to be very secure against tampering if used correctly -- so what's the secret?
Hash validation aka MAC (Message Authentication Code). The ASP.NET MAC and usage is discussed briefly this article. In short the MAC is a hash of the form data (built using a server -- and perhaps session -- secret key) which is embedded in the form as a hidden field. When the form submission occurs this MAC is re-calculated from the data and then compared with the original MAC. Because the secrets are known only to the server it is not (realistically) possible for a client to generate a valid MAC from the data itself.
However, I do not use RoR or know what modules, if any, may implement security like this. I do hope that someone can provide more insight (in their own answer ;-) if such solutions exist, because it is a very powerful construct and easily allows safe per-form data association and validation.
Happy coding.

Resources