I do not understand how to use ranked-model - ruby-on-rails

I'm using the gem ranked model : https://github.com/mixonic/ranked-model
I want to use it to allow my users to order articles in a back office.
From what i understand from the gem the stored value in DB is different from the _position who is the real rank if you start counting your elements.
Now if i have an array of images, i display them and i want to increase or decrease their position of one rank. i would do a row_order_postion +1 or -1. But i cannot know their row_order_postion (it returns nil), only their row_order.
My question is : what use is there to be able to change by row_order_position if i cannot know my row_order_position ?

I too find this a little weird, but if you are iterating over an array, one work-around is to do a each_with_index. This way you can store the position somewhere (in a hidden field for instance), and when performing the rearrangement of the articles pass the new position + or - 1.
# Something like this
<% Article.each_with_index do |article, index| %>
<%= render :partial => 'article', :locals => {:article => article, :position => index} %>
<% end %>

Related

Active Records - List the item's index number (the actual order number in which the item is displayed)

I'm a newb to Ruby on Rails and I have a issue trying to display the index number of a record set. I've had a good search, but can't find the specific answer.
The issue I'm having is that I cannot find a way to output the index number from a recordset which i'm displaying.
[Title], [Description]
[Title], [Description]
[Title], [Description]
etc.
So to clarify, I'm looking to output the order (index) number (1, 2, 3) in a list where i'm able to output the Title & Description, also i'll point out the obvious - this is different to the unique ID which may be stored in the database, thus if i was to filter or sort the results I would still want to show the order (index) number (1, 2, 3 etc).
I have founds example of where they loop through the results and incrementally add to a pre-defined index value. The problem is my app doesn't use a loop statement to output the records, instead it's using an Active Record to display (and essentially loop through) the results. From what I understand, Active records will automatically loop through and output the records by rendering the code snippet ie. <%= render #links %> This works great for my example - For the full code for the app, please refer to the tutorial I deprived the app from:
https://www.codementor.io/danielchinedu/building-a-basic-hacker-news-clone-with-rails-5-4gr4hrbis
So in retrospect, I'm looking to clone the app in the tutorial but add an order number to the link lists.
From the documentation:
Rails also makes a counter variable available within a partial called
by the collection, named after the member of the collection followed
by _counter.if you're rendering #products, within the partial you can
refer to product_counter to tell you how many times the partial has
been rendered.
guides.rubyonrails.org
So you can use something like <%= link_counter %> in your _link.html.erb partial. Hope this will help.
The tutorial actually explains it quiet well, i think.
Underneath the <%= render #links %>:
We are using a rails feature here, when you call render on an instance variable holding an active record relation object, it will iteratively loop through the object and then render the appropriate partial.
So! It's a Rails feature making it easier to write partial loops!
<%= render #links %> is the same as: <%= render partial: 'link', collection: #links %>
If you want an index in your partial, you can just append a local variable. For example:
<%= render #links, locals: {num: 1} %>
And then in your view after you have written the variable, remember to add 1 so it's ascending.
<div class="link">
<div class="title">
<%= num %>
<%= link_to link.title, (link.url? ? link.url : link) %>
...
<% num += 1 %>
Good luck!

Rails and factory patterns

Imagine a Rails project that looks up animal celeberties based on their names. This Rails app is backed by an external service that does the actual lookup. The service returns back results based on a key. For example, if I make a request to this external api like [GET] /animal?name=benji, I would get back something like {"type":"dog", "legs":"4", "tail-length":"short", "collar":"blue"}. However, if I pass in ...?name=flipper to the animal endpoint, I would get back {"type":"dolphin", "color":"gray", "food":"fish"}. (The data is returned in actual JSON or XML. I am just using pseudo code here to communicate the point.)
My first question is this... Given that the attributes of the return call vary based on data which is passed in, when unmarshaling a response (for lack of a better term) into a "model" object, does it make sense to implement some type of factory pattern (ala Design Patterns in Ruby, by Russ Olsen, Chapter 13) to create objects of an appropriate class? Are there other approaches that would make sense?
My next question is this, lets say that I want to display a list of all animals on a web page (using ERB templates.) Does it make sense to create different partial templates (eg _dolphin.html.erb and _dog.html.erb) and then put a case in the main list view that can deligate rendering each list item to an appropriate template.
For example:
list.html.erb...
<ul>
<% for animal in #animals.each %>
<li>
<% if animal.type == 'dog' %>
<%= render :partial => 'dog', :locals => {:animal => animal} %>
<% elsif item.type == 'dolphin' %>
<%= render :partial => 'dolphin', :locals => {:animal => animal} %>
<% else %>
<%= render :partial => 'generic_animal', :locals => {:animal => animal} %>
<% end %>
</li>
<% end %>
</ul>
(Here animal.type=='dog' is intentional. I am not using a symbol (:dog) because the data returned back from the API is a string value, and it is used to populate the animal.type attribute. Bad, I know.)
The project that I am working on is using this approach right now. (Obivously, I have changed the elements/domain.) I am wondering if this is a valid approach, and/or if others have dealt with similar problems and how they went about it.
Thanks!
I'd say create a single model and a single view which contains all possible attributes (can't be an infinite number ;) ).
And then you have an
if attribute_x exists then
display it
end
if attribute_y exists then
display it
end
for each attribute.
If you create a view for each animal this wouldn't be DRY at all, 'cause you'll repeat yourself sooo many times, just knowing that each animal has favorite food and a color, etc.. Another reason: If the API changes a bit, and an animal gathers or looses an attribute you would have to adapt this change.
With just one view, it would be all fine for all time.
If you want to be super-sure that you gather all attributes, you could place an array of all known attributes inside your controller and if there's something unknown: write it to a log file.
I'd only choose the way of 'one view per animal' if you want to be able to display things completely different for some animals. But then you could also tell your controller that it should choose another view if name = 'Donkey Kong'. you know what I mean.

Assigning session variable without saving to a database

I'm trying to set up something like a shopping cart, and what I have is a form to choose how many of each size shirt someone may want to buy. I plan on having XXS, XS, S, M, L, XL, and XXL, and they choose a quantity of each and add it to the cart. The simplest way I could think of doing this was to have a t.integer "size_??" for each size in a database for 'tshirt', and after the user clicks add to cart, that digit is then stored in the session until their payment is approved, in which case it is then saved to the database. Then someone could view the database later to see what orders were placed. (This would be for making custom shirts, so there would be no 'examples' saved on the database that need to be viewed or listed).
My first question is if that is correct. I thought of using a "Shirts has_many Sizes" approach, but I'm not the most familiar with it, and if all I am trying to achieve is to obtain a quantity, I feel like that might be a little overkill.
My second question is this: if I have a form to select the quantity of each, how do I assign the values to the session variable. My first thought was an approach like this:
new_order.html.erb:
<%= form_for(:shirt, :url => {:action => 'save_to_session'}) do |f| %>
<table summary="New shirt order form">
<tr>
<th>Size S:</th>
</tr>
<tr>
<td><%= f.select(:size_s, 0..99) %> </td>
</table>
<%= submit_tag("Place order") %>
<% end %>
and on the controller side
def new_order
#temp_order = Shirt.new
#temp_order.size_s = 0
end
def save_to_session
session[:shirt_size_s] = #temp_order.size_s
redirect_to(:action => 'show_session')
end
def show_session
end
where show_session just spits out the session[:shirt_size_s]
The error Im getting with that is
undefined method `size_22' for nil:NilClass
Perhaps this is taboo, as it seems to be a very simple approach, but this will not change the session value. If I add the following line to save_to_session it works, so I'm thinking perhaps save_to_session doesn't have access to #temp_order, but I'm still in the learning phase and don't exactly know what is going wrong here.
session[:shirt_size_s] = 12
Your #temp is an instance variable normally used for populating the page and not used for retrieving data. You want to user the params[:field1] for that where field1 would be the name of your select.

Need help modifying multiple models example from advanced rails recipes book

I'm developing a simple rails app for my own use for learning purposes and I'm trying to handle 2 models in 1 form. I've followed the example in chapter 13 of Advanced Rails Recipes and have got it working with a few simple modifications for my own purposes.
The 2 models I have are Invoice and InvoicePhoneNumber. Each Invoice can have several InvoicePhoneNumbers. What I want to do is make sure that each invoice has at least 1 phone number associated with it. The example in the book puts a 'remove' link next to each phone number (tasks in the book). I want to make sure that the top-most phone number doesn't have a remove link next to it but I cannot figure out how to do this. The partial template that produces each line of the list of phone numbers in the invoice is as follows;
<div class="invoice_phone_number">
<% new_or_existing = invoice_phone_number.new_record? ? 'new' : 'existing' %>
<% prefix = "invoice[#{new_or_existing}_invoice_phone_number_attributes][]" %>
<% fields_for prefix, invoice_phone_number do |invoice_form| -%>
<%= invoice_form.select :phone_type, %w{ home work mobile fax } %>
<%= invoice_form.text_field :phone_number %>
<%= link_to_function "remove", "$(this).up('.invoice_phone_number').remove()" %>
<% end -%>
</div>
Now, if I could detect when the first phone number is being generated I could place a condition on the link_to_function so it is not executed. This would half solve my problem and would be satisfactory, although it would mean that if I actually wanted to, say, delete the first phone number and keep the second, I would have to do some manual shuffling.
The ideal way to do this is presumably in the browser with javascript but I have no idea how to approach this. I would need to hide the 'remove' link when there was only one and show all 'remove' links when there is more than one. The functionality in the .insert_html method that is being used in the 'add phone number' link doesn't seem adequate for this.
I'm not asking for a step-by-step how-to for this (in fact I'd prefer not to get one - I want to understand this), but does anyone have some suggestions about where to begin with this problem?
There is a counter for partial-collections:
<%= render :partial => "ad", :collection => #advertisements %>
This
will render "advertiser/_ad.erb" and
pass the local variable ad to the
template for display. An iteration
counter will automatically be made
available to the template with a name
of the form partial_name_counter. In
the case of the example above, the
template would be fed ad_counter.
For your problem of detecting whether a row is the first one or not, you could add a local variable when calling the partial:
<%= render :partial => 'mypartial', :locals => {:first => true} %>
As it would be much easier to detect in the main file, whether a row is the first or not I guess.
Instead of detecting whether a phone number is the first, you could also detect whether a phone number is the only one. If not, add remove links next to all numbers otherwise, do not display the remove link. Note that besides showing/hiding the link, you also need to add code, to prevent removing of the last number by (mis)using an URL to directly delete the number instead of using your form.

Refactoring a simple method in my controller

I'm having a tough time deciding how to refactor this method in my controller. The idea is that (in this case) it graphs the users that joined (or were created) in the past two weeks.
You might be wondering why I did the #graph_limit thing, and that is because I always want the day that has the most results to be the tallest bar on my bar chart (which in the view are just created with css by making the height of the <div> using css).
Basically I want to dry it up and... ya know just about improve this method as much as possible:
# Controller
def index
two_weeks_ago = Date.today - 13.days
#users_graphed = User.count(:conditions=>["created_at >= ?", two_weeks_ago], :order => 'DATE(created_at) DESC', :group => ["DATE(created_at)"])
two_weeks_ago.upto(Date.today) do |day|
#graph_limit = 100/#users_graphed.values.max.to_f
#users_graphed[day.to_s] ||= 0
end
end
Also I should mention, that you guys are probably going to rip my code to shreds... so I'm bracing for the outcome.
# View
<% #users_graphed.sort.reverse.each do |user| %>
<li>
<% content_tag :div, :style => "height: #{number_with_precision(user[1] * #graph_limit, :precision => 2)}px; ", :class => "stat_bar" do %>
<%= content_tag(:span, user[1]) unless user[1] == 0 %>
<% end %>
</li>
<% end %>
Ultimately and what my real goal here is to put this into my application controller and be able to chart any models by it's create_at times. maybe something like tasks.chart_by(2.weeks). How would you guys get this separated out into something I can use throughout the whole app?
I agree with Joseph that your controller here is doing a lot of work that should be done in the model. Any time you're specifying multiple find parameters in your controller, ask yourself whether or not that should be in your model instead.
You're doing a lot of iterating here that seems needless. Firstly, You shouldn't be calculating #graph_limit inside the loop. You're recalculating it 14 times, but the value is going to be the same every time. Do that outside the loop.
Secondly, that sort.reverse in your view sticks out. You're already sorting in your finder (:order => 'DATE(created_at) DESC'), and then you're sorting again in your view and then reversing it? You should instead be asking the database for the values in the final order you want them. Then to make your zero-filling code work you can just reverse it, doing Date.today.downto(two_weeks_ago) instead of upto.
I would say that you should really be doing this all in SQL, but unfortunately (as perhaps you've discovered) MySQL makes it difficult to fill in missing days without creating a calendar table to join against.
Thanks Jordan, per your ideas (which were really great by the way) I've created a helper that is like such:
def graph_by_time(klass, time_ago)
time_range_start = Date.today - time_ago
#elements_graphed = klass.count(:conditions=>["created_at >= ?", time_range_start], :order => 'DATE(created_at) DESC', :group => ["DATE(created_at)"])
#graph_limit = 100/#elements_graphed.values.max.to_f
time_range_start.upto(Date.today) do |element|
#elements_graphed[element.to_s] ||= 0
end
return #elements_graphed.sort.reverse
end
The biggest issue here is zero filling the days which have no records associated with them, your method of switching to from upto to downto didnt work and only returned the records which did result in a integer other than zero.

Resources