Rails beginner help with making a custom order form - ruby-on-rails

So what I basically would like to do is make an order form. In this order form, users will submit information about their company members, and there will also be a part at the end of the order form where the user can select option services that are added by my client from a backend. I am new to rails so I would just like someone to help me make sure that I am going to do this using the best possible practice. Here is what I think I need to do:
Make an Order model
Make a Member model
Make a Field model
In my order model make
has_many :members
has_many :fields
In my member model make belongs_to :order and in my field model make belongs_to :order. Then what I need to do in my orders controller is #fields = Field.all and extract it in a #fields.each block.
P.S. there is one other thing I have to do and that is to make the order form displays 3 types of headers: Corporation, LLC, and Non-profit. What I thought would be smart is if in the url I made it like type=1 and type=2 and type=3 and in my model use an if statement like:
def order_type(type)
if type === "1"
"corporation"
elsif type === "2"
"llc"
else
"nonprofit"
end
end
I think using an if statement is kinda sloppy so for that so if someone could please explain to me the best practice that would be great. Please don't try and answer with a railscasts episode because I have checked out a lot of them already. What I would like is an explanation if possible
Thanks guys it means a lot

Are your fields really so complicated that they require their own model? If your "fields" are a one column list just include them as a column in the model they are applied too.
The problem with using the URL method that you suggested is as follows. Say a user creates a model object and assigns it as a "LLC.". Then your URL would have type=1 somewhere in it as you suggested. Now a user bookmarks this URL and afterwards someone realized it was a typo and instead of LLC the heading should be something else. Now you have a unRESTful situation where you are creating dead links on the Internet or you have urls that are encoded. URL encoding should be used for querying data not dceciding what the data should be.
I would not mess with the urls for the heading question. Instead just add a heading field to the main model that will be displayed and have that field either be LLC etc. Then in the view just have a variable that loads in the value for the object being displayed.

Related

retrieving Polymorphic association correct model and id

I am new to polymorphic associations and am a bit lost at the moment.
I am trying to add a functionality to my website where users can ask quotes to different types of professionnals. Each professionnal own a specific type of quote such as :
model photographer own photographerquotes
I did this because each type of quotes is very dependable to the professional model it belongs to, and database model is specific for each.
Though one thing is constant with those quotes models : they can be commented either by the user or the professionnal
I have then made the different quotes models commentable but I am a bit lost when I need to create the commentable variable inside the comments controller #create action.
this tutorial says we can make modules https://gorails.com/episodes/comments-with-polymorphic-associations (at 15:00)
And the railscast here: https://www.youtube.com/watch?v=WOFAcbxdWjY grabs the correct quote model directly from the address bar with his following trick :
def load_commentable
klass = [Article, Photo, Event].detect { |c| params["#{c.name.underscore}_id"] }
#commentable = klass.find(params["#{klass.name.underscore}_id"])
end
actually both seem counterintuitive to me as I am no Ruby expert, nor even Rails expert yet.
Is one of the two solutions a desired solution ? Are there some more possibilities in 2017 developping under rails 5 ?
EDIT
Actually there would be a third possibility :
it is to pass the quote model type and id inside the form as hidden fields. But it the gorails tutorial the person says that anybody can pass these data for us and this is not really safe ...
I am no HTPP expert but I thought the CREATE method couldnt be replicated inside the address bar. I thought only the GET method was 'public'...

Reform to wrap a collection of records to be saved simultaneously

I have a collection of records that are related to a specific parent object. I have no need to update the parent, just want to update all the children.
I tried making a Reform::Form and simply adding the collection declaration. An example might be a developer has many projects. When the developer goes on holiday his projects are "on_hold", but some of them might still be active. So, I would like to list all the projects with a checkbox to check if they should be put on hold. I essentially want to have a simple_fields_for :projects.
Does it make sense to use Reform for this?
I have this code:
class ProjectsForm < Reform::Form
collection :projects do
property :name #perhaps I want to rename them in the same form (bear with me)
property :on_hold
end
end
This should work, but when initializing the form with a hash
#form = ProjectsForm.new({projects: #array_of_projects})
I get an error
undefined method `projects' for #<Hash:0x007fce8f2783b8>
As if the collection method is not working. I am obviously doing something stupid.
I'd really like to use Reform. I love the philosophy behind a lot of the trailblazing suit of gems. It will be great if someone can point me in the right direction here.
If it turns out this isn't a good usecase for Reform I'll find another :P
Update:
I think reform is slightly more coupled with the idea of a model than what I thought. I thought it was just a form object with properties to play nicely with form builder. I now find that your model is key. You need to initialize the Reform form with A model, or in the case of composition, a few models. However, if you pass in an array of hash reform believes this is the model and then tries to access the :projects property on the model. In my case a hash.
I have now added a
# my contrived example is getting lame
attr_accessor :projects
to the developer class and it is working. At least the generating the reform object is working.
I am still curious wether this is a good use-case for Reform.

Rails: Joining multiple temp fields before save

So basically I have a Book model that contains information like
book title (string)
author (string)
description (text)
etc.
Now on the front end I added the capability of dynamically adding more author fields. In the form I named it book[coauthor], so if I add a second author, I'd have a field book[coauthor][name_0].
What I'd like to achieve is combine book[author] and all book[coauthor][name_i] to one big string and separate each name with ,. (i.e. so with book[author] being "Alice" and book[coauthor][name_0] being "Bob" I should get "Alice,Bob" saved in book[author] in DB)
First Question: At this point is it better to do it with Javascript or with Rails (in controller)?
Second Questiion: Currently I'm doing it in the Rails controller but got Can't mass-assign protected attribute 'coauthor'. However I don't want to make it a real attribute since all I need is some processing before saving all the information to the author field. What should I do?
Thanks guys.
The first question is a bit loaded, and you might get some heated disagreements but the way I look at it is this. Not everybody runs with Javascript enabled, so I make sure I have a way to do everything through Rails myself.
In this case, it's actually quite easy.
The first thing I recommend is to not send the coauthors as part of the params[:book]. Instead, send them separately as a params[:coauthors]. Then in your controller you can do this:
#book = Book.new(params[:book])
#book.author = ([#book.author] + [params[:coauthors]]).join(",")
#book.save
Actually, it may be better to update params[:book][:author] since that would work for both create and update. Either way, I hope that helps.
To the first question: I don't see any compelling reason to one over the other. JavaScript might provide a bit more ability to validate/fix formatting in this field in 'real time', but I'm not sure that's particularly important.
To the second question: You don't need to make something a real attribute in order to make it accessible. You've presumably created coauthor as a virtual attribute using attr_accessor, but this doesn't automatically add it to the mass-assignment whitelist. To do that, also add it to your attr_accessible list.

Solution to insert cross-hyperlinks of Rails objects inside text attribute (as HTML)

I am working at a web app in Rails that behaves much like a CMS: there are articles, which have a text attribute that contains links to other articles (or other object classes). I'm currently storing the attribute as HTML.
Is there any good way to model those links in a way that is relative easy to change, and contain the reference to the object id, instead of the absolute url?
One of the solutons I was thinking was to use some kind of special mark-up, such as:
[link_to "Text for the link", Article:12]
where 12 is the id of the article it links to. This mark-up will be parsed when the text is rendered.
The downside of this is that I have to hack into TinyMCE (the editor I'm thinking of using to edit the HTML) so that it can insert links to other objects, by accessing the database and automatically assigning the object type and ID (the person who's editing the texts doesn't know the id's).
Is there any simple solution to this?
Or should I stick to using absolute urls (which, besides maintenance issues, is annoying in development, as they will always point to production and that is confusing for me)?
Additionally, does anyone have similar examples in other languages (php, Wordpress, other CMS, etc) that tackle this problem in a nice way? I'm thinking, this is pretty vital in CMS, and can reduce a lot of man hours if a nice system can handle all those links.
EDIT:
Another possible solution that I'm thinking about is letting the person copy the link of the article directly in the code, but it should, upon submission, generate the correct association id and make it so that if the url structure changes, the link is always up-to-date. I'd like to hear your opinions and experience with this approach, if you have tried it.
The challenge with this approach is parsing the link with Rails and finding out that it points to an Article, and that article has the id ##. Then I have to insert a shortcode that will always translate, upon parsing and rendering, to an actual link to that article.
I found a method that could make this feasible:
Rails.application.routes.recognize_path
But there may be some caveats that I don't see right now...
EDIT no. 2
I also want to specify that I chose CKEditor as the content editor, but I will consider other ones if there are clearer advantages.
I have built something similar using a shortcode system which would allow me to call specific methods on the model and replace the shortcode in the text:
Helper
def parse_shortcode(model)
text = model.text
text.gsub(/(\[#!\s?\w+\])/i) do |match|
result = model.try(match)
result.nil? '' : link_to(result[:text], result[:url])
end
end
Model
def contact_link
{ :text => self.name, :url => self.url }
end
View
<%= parse_shortcode(#article) %>
I haven't tested the above code and it is obviously a bit simplified but it explains my thought process behind this.
EDIT: Just to clarify my above example uses an invented shortcode syntax of [#! method]
An article can have many related_articles and at the same time this article can be related by many other articles, so it is best to model this as a many-to-many relationship.
One way to define this type of relationship in Rails is has_many :through.
To use has_many :through you have to create a join model, perhaps call it ArticleRelation. This model will have two fields, an article_id that represents the current article and a related_article_id that represents the article who’s being referred as related.
class Article < AR::Base
has_many :article_relations
has_many :related_articles, :through => :article_relations
end
class ArticleRelation < AR::Base
belongs_to :article
belongs_to :article_relation, :class_name => 'Article'
end
When creating self-referential relationships it’s important to remember that you’re only creating one side of the relationship. Although article_1 might list article_2 as related, there is no way to for article_2 to list article_1 as related. You'd need two ArticleRelation records to create a mutual relationship.
It’s difficult to think up appropriate names to define another side of the relationship so you can prefix both with the word “inverse” to give inverse_article_relations and inverse_related_articles. You also need to specify some additional options to make the relationships work. For inverse_article_relations you’ll have to specify the name of the other model as it can’t be inferred from the relationship name and you’ll also have to define the foreign key as related_article_id. For the inverse_related_articles relationship you need to specify the source as articles, as again it cannot be inferred from the name of the relationship.
has_many :inverse_article_relations, :class_name => "ArticleRelation", :foreign_key => "related_article_id"
has_many :inverse_related_articles, :through => :inverse_article_relations, :source => :article
Test it out, this should work for you per current requirement.
A solution I've seen in a lot of other CMSs is a combination of custom file browser in TinyMCE and page rewriting (similar to freakyDaz's answer).
TinyMCE has documentation and example code for implementing a custom browser. You'll have to provide the backend pieces, of course.
CKEditor has documentation for a similar feature as well.
Have your backend implementation return something that's easy to parse for the URLs (urlfor:Article:12, for instance), then have your rendering code replace those with actual URLs.
I just thought of another possible solution for the use case:
The admin user specifies the relationships before they edit the text (using Chosen.js, this can be done in a user-friendly way).
Then the person either submits the form to save the model or it can be done asynchronously.
When the relationships are saved, a shortcode is generated and displayed for each of them, and that short-code can easily be pasted in the text.
When displaying the text in the front-end, the text will be parsed for the shortcode, in a similar way to what #freakyDaz is suggesting.
In this way, I don't have to hack or create custom actions in the editor. I think it's a pretty pragmatical approach, but I'd like to hear your opinions. Of course, the admin who's making the text should be educated to follow the process in that order, but in my case very few persons can be admins (1 or 2), so it's manageable.

How can I display data from a related model in a Rails form

I have two models
class Car
has_many :engines
end
class Engine
belongs_to :car
end
In the car form I have a select field where the user can select the engine type. The list might be "1.4L; 1.6L; 2.0L..."
Lets say I want to display additional information from the Engine model when the user selects a engine. This should be displayed on the Car form. This might be e.g. BHP, max revs, ...etc
How do I set something like this up. I guess there are two aspects:
How to display data from the engine
model on the car form without using
a field (this data is not editable).
How to update this data dynamically
when the user selects an option in
the select field.
Can anyone point me towards a starting point for this. I'm a bit lost where to begin.
Many thanks!
If you're working with collection_select in your form, you are setting two arguments, like :id and :name in your collection_select call. :id is the method called to determine the value for the option tag and :name is the method used to display the option tag's content.
Solution: Create a method (e.g. :name_for_select) in your engine model which returns a string with more information about your engine and call collection_select with :id, :name_for_select instead.
This is called a nested form, and if you google that you will find a lot of hints and tips. E.g. check out this and this tutorial.
You should also consider using a form builder like formtastic or simple_form: they have a lot of helpers to make life easier for you.

Resources