Adding new functionality to Spreecommerse - ruby-on-rails

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

Related

Complicated Website Structure - 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.

Display Deal Page for non-admin (.i.e. standard visitors) with Rails 4/Active Admin

I'm developing a Daily deal app on ruby on Rails to learn it.
I have installed active admin to create as admin, Users and Deals.
It's working great but what I don't understand (as I'm quite new to Ruby on Rails), is:
Does Active Admin generate on its own the routes for Deals so that I already would have at my disposal for each deals like: myapp.com/deals/deal_id=456 (deal 456) ? It would be a page describing the deal n° 456 ?
I see if I do 'rake routes' that I have the following route that has been created by Active Admin: myapp/admin/deals/:id which is my view page of the Deal on Active Admin, but that's not what i want to do. I want to display a view for the website visitors for the Deal. It should be on something like myapp.com/deals/456
I need to be able to AUTOMATICALLY create a route/url (such as myapp.com/deals/456) that web visitors can see as soon i create the Deal in my admin interface on Active Admin.
Should I just create a 'show' method on the DealsController ?
How to manage the generate a 'route/url' part every time I create a new Deal in my admin interface on Active Admin ?
If you know of any web resource explaining this, that would be appreciated too as I am a newbie (note that I could also use guidelines for: a blog it would be how to generate the route/url to display a certain blog post after creating it on AA, or for a ecommerce website it would be to create the route/url for a Product Page)
Rails basics then:
:id stands for the value of the id column of the model you are viewing/editing/handling/whatever.
So, if you want your route to respond to
/deals/deal_456
You have to instruct it to do exactly that:
match '/deals/deal_:id', to: "deals#show", via: 'get', as: :view_deal # or path of your like
Then, all the below will be equivalent, in terms of what you see
#deal=Deal.find(456)
redirect_to view_deal_path(#deal) # myapp.com/deals/deal_456
redirect_to deal_path(#deal) # myapp.com/deals/456
redirect_to #deal # myapp.com/deals/456

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.

How to organize actions that don't fit into the normal MVC

I am creating a survey application so I created a surveys controller that behaves very restfully creating, updating, etc a Survey. However now I'm adding other actions to it like 'take', for taking a survey, and 'share', for sharing a survey. There are more actions too. I'm starting to wonder if I should organize my code differently and move those new actions into their own controllers however I'm not so sure take or share or some of my other actions really fit into REST really well. They almost make more sense as actions if I wasn't a little worried about the survey controller size.
Either I could leave it the way it is or I was thinking of creating a survey namespace and creating like Survey::TakeController and the Survey::ShareController. Then I would then I guess use the new action or index?
I'm not exactly sure the proper way to do it. If I do create a survey namespace should I then move the orginal SurveyController in it? That would make some weird looking methods like survey_survey_path.
To think RESTfully, you should probably stop thinking of them as "controllers with actions" and start thinking of them as "objects that can be created/updated etc" - controllers are just proxies for the views that show the results of creating/updating an object.
A lot of the time, I've found that an extra action is really just a variation of "update" - just with its own special requirements (eg only certain people can update it or whatever). That sort of logic can often go inside the model itself (eg "MyModel#can_be_edited_by?(some_user)").
Sometimes you find that actually you have an extra "hidden" model that needs its own RESTful interface.
Eg with your "take" a survey - I'm guessing, but what you have is something like a "SurveyResult" and a person can "create "survey" but when they "take" a survey, they are actually creating a "SurveyResult" (the other commentor called this a "SurveyParticipation" - but it's the same thing).
The thing being that you will probably have multiple SurveyResults that each belong_to :survey and belong_to :some_user_model.
Then you can set up nice restful routes such as:
/surveys/123-my_favourite_colour/results
which will return a set of all the results for a single survey
This is actually the RESTful way to view this part of your object-space.
As to sharing a survey - that's a more intersting question. It depends on how you've got your authorisation setup for "sharing". It also depends on what you mean by "share".
Are you sharing the results of a survey, or sharing the survey-object itself (so another user can edit the questions) or are you (as a person that has just taken part in a survey) sharing a link to the survey so that your friends can also take the survey?
For the first two above - I'd consider a "SurveyPermission" class that belongs_to :survey and belongs_to :some_user_model.
The you can create a SurveyPermission for another user - and Surveys can be edited by the creator- or anybody that has a permission to edit it.
Thus the share action is to create a SurveyPermission.
Though to be honest - your SurveyPermission is likely only to be used for create and delete, so it may be simpler to stick those two actions in the Survey controller.
For the latter - well, that's just sending a "create_survey_result(#survey)" link to somebody...
Update:
I don't generally bother with namespaces unless there are two resources named the same thing (but in different contexts). You only need namespaces to disambiguate between them and that doesn't seem to be the case here.
In this case - the only namespacing that occurs is in the routing:
map.resources :surveys do |s|
s.resources :results
s.resources :shares # ???
end
gives such routes as:
new_survey_path
surveys_path
new_survey_result_path(#survey)
survey_results_path(#survey)
If you want to stay with the RESTful approach then you could have a SurveyParticipation resource, with the new/create actions being invoked when somebody takes a survey. And a, ahem, SurveyShare resource for sharing a survey. That name's pretty awkward though!
Personally I don't think it's the end of the world if you have to augment your existing controllers with a small number of additional actions, as long as it doesn't get out of hand. RESTful Rails saved us from the bad old days when controllers had tens of actions.

Rails-way of structuring admin/user/public controllers

A fictitious Rails app has the following resources:
Photographers
Images
Comments
A Photographer has many Images, that have many Comments
Each photographer has a login and is able to view, upload, edit and delete their images, comments as well as their own profile.
An administration interface is available and can edit both images, photographers and comments.
Furthermore, the photographer, their images and their comments are available from a public interface without login where visitors can add comments.
My question is: What is the Rails-way of structuring the controllers? I was thinking of going with namespaces for each 'role' (public, account, admin) like this:
# For administrator
Admin::PhotographersController
Admin::ImagesController
Admin::CommentsController
# For a logged in photographer
AccountController (?)
Account::ImagesController
Account::CommentsController
# For public
PhotographersController
ImagesController
CommentsController
However - some of the methods of these controllers are overlapping. Is this the best way, even though it's not that DRY?
Thanks!
If they are overlapping, you could extend the base controllers into the account/admin namespaces. eg you do your ImagesController which is for the actions everyone can see. This extends ApplicationController as normal. Then you do your admin version of ImageController, and that extends ImagesController. Then you add/override methods in the admin version for the required different behaviours, or it may just be as simple as adding a couple of before filters such as require_admin for example, which checks that current_user is an admin user, and redirects them away if not.
as you said, this is not really DRY. at least, you could structure routes and controllers to act for all the requirements, for example:
resources :photos, :only => [:index, :show] # offer only index and show actions to public
scope "/admin" do
resources :photos # full access for logged in users
end
if you also need index and show actions, you can add some checks inside them to load a different view (say you have a public and admin layouts).
another way could be to have a unique layout, no /admin/ sections and offer editing features to logged in users. so if logged and owner of some photo, allow editing and show context links. it's a matter of tastes :P

Resources