Manually Populating select_tag in Rails - ruby-on-rails

I know this looks insane, but I'm trying to do something like this:
def site_list
#sites = []
Dir.new(base_dir = "./app/sites/").each do |name, idx|
path = "#{base_dir}#{name}"
if FileTest.directory?(path) && !name.starts_with?('.')
#sites << name
end
end
return #sites
end
Just stay with me through my craziness.
There's a URL parameter called :site, if this exists then I want a manually created on-the-fly drop down list to have the correct element selected. So, for this to work I need to create an associative-array/hash that does id > name.
Step-by-step:
List all the directories associating id > name (id is made up, it's just the index)
Based on the :site URL parameter, select the correct element in the select list
There is no step 3.
So you then have: <%= select_tag :sites, options_for_select(site_list) %> but with the third parameter as the option to select.
Hopefully this makes some sense at least!
A description in English
I am populating an array based on folders in the file system
This array is to populate a select_tag
The select_tag's currently selected item is to be determined based on the current route. (:site)
If the route doesn't contain :site then display the select_tag with "Please select..." or something along those lines.

In select form helper you can actually pass a hash and keys will be displayed in select menu and values will be the values. So let #sites be a hash, smth like this:
class SitesController < ApplicationController
...
#sites = { :site1 => 'stackoverflow', :site2 => 'someothersite' }
...
end
So to filter this hash with site get param let create a helper based on Hash#keep_if method http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-keep_if :
module ApplicationHelper
def site_list(sites, site)
sites.keep_if { |name, value| value == site }
end
end
just change the condition in the block to work for you. And code in form will look like:<%
<% site = 'stackoverflow' %>
<%= site_list(#sites, site).inspect %>
<%= f.select :name, site_list(#sites, 'stackoverflow') %>
Even better way to do it without helper is to use before_filter in your controller:
class SitesController < ApplicationController
before_filter :extract_select_option, :only => :your_action
protected
def extract_select_option
# SITES - all sites readed from file
#sites = SITES.keep_if { |key, value| value == params[:site] }
end
end

Related

Linking to the latest post from home page in a Rails application

I'm building a Rails app where I have individual entries called films. I would like to display the latest entry's link on the homepage (separate controller) and I'm struggling to make it work.
My films_controller.rb is as follows (excerpt):
def show
#film = Film.find(params[:id])
end
My home_controller.rb only has the following:
def index
end
And my view file (index.html.erb) has the following:
<%= link_to #film.last.filmTitle, film_path(#film) %>
I'm getting the following error:
Couldn't find Film with 'id'=#<Film::ActiveRecord_Relation:0x007fc93f2d1fd0>
With the #film.find(params[:id]) highlighted.
Thanks!
The last method:
Find the last record (or last N records if a parameter is supplied). If no order is defined it will order by primary key.
source
You can add a #last_film instance variable in your index controller and use it in the view.
def index
#films = Film.all
#last_film = Film.last
end
and in your index.html.erb
<%= link_to #last_film.filmTitle, film_path(#last_film) %>
The index method need something, currently, it didn't connect with ActiveRecord like model or table, that's why
Couldn't find Film with 'id'=#<Film::ActiveRecord_Relation:0x007fc93f2d1fd0>
So if you need to show recent posts in the index then you could something like this
def index
#films = Film.limit(10).order(created_at: :desc) #=> or you can use id
end
it will show last 10 records, for this in the index.html.erb like this
<% #films.each do |film| %>
<%= link_to film.filmTitle, film_path(film) %>
<% end %>
In the other hand if you need to show only one post which is the last then you should modify this query like this like limit(10) to limit(1) or you can use use the last method like this
def index
#film = Film.last
#or
##films = Film.limit(1).order(created_at: :desc) #=> or you can use id
end
if you use this #film = Film.last then your index file will like this
<%= link_to #film.filmTitle, film_path(#film) %>
otherwise, you need to use each method which describes before.

Rails: simple 'Setting' model with key, value attributes

I’m new to rails and I’ve a pretty simple situation to solve but I cannot figure out how to proceed with it.
I want to create a simple ‘Setting’ model with key, value attributes. ‘SettingsController’ may contain 2 public methods only index and update. Only index action will have a view file with a form whose fields will represent each record of the ‘Settings’ table.
I want to be able to define some permitted keys (may be using some private method) and I want the form to create or update the record of relevant fields on submitting the form to update action.
Now, I don’t know exactly what code should I use in controller for index and update actions and in the index view file for the form which can create/update multiple records at the same time and can show updated values all the time. How do I proceed with it?
Update # 1:
I've managed to write some controller actions as below (based on some tutorial):
class Admin::SettingsController < ApplicationController
def index
#settings = Setting.all
end
def update
setting_params.each do |key, value|
Setting.where(key: key).first.update_attribute :value, value
end
redirect_to admin_settings_path, notice: "Settings saved."
end
private
def setting_params
params.require(:settings).permit(:site_title, :site_desc)
end
end
The form code in index view template is given below:
<h1>Settings</h1>
<%= form_tag admin_settings_path, method: "put" do %>
<p>
<label>Site Title:</label>
<%= text_field_tag "settings[site_title]" %>
</p>
<p>
<label>Site Description:</label>
<%= text_field_tag "settings[site_desc]" %>
</p>
<p>
<%= submit_tag "Save settings" %>
</p>
<% end %>
This forms saves the values correctly in the database but the saved values doesn't persist in form fields.
Maybe the problem is in the index action. Setting.all returns an array of Setting record, not a hash like { key1: value, key2: value } which I think you are trying to achieve. The form, therefore, displays data improperly. You can try this:
def index
#setting = {}
pairs = Setting.pluck(:key, :value)
pairs.each { |key, value| #setting[key] = value }
#setting
end

Creating links based on unique attributes from a Rails class

My Rails model 'Product' has a string attribute called 'Brand'. I am trying to create a list of links consisting of the unique brands I have as attributes in my products. I am able to list the unique brands, but need help setting up the routes/controller structure. In the StoreController I have a method 'brand' that should trigger the filtered #products based on my :brand parameter. It looks like this:
My StoreController:
def index
#brands = Product.uniq.pluck(:brand)
end
def brand
#products = Product.find(params[:brand])
render 'store/index'
end
My view:
<% #brands.each do |brand| %>
<%= link_to brand, brand_store_path(brand) %>
<% end %>
My routes.rb:
resource :store do
collection do
get ':brand' => 'store#brand', :as => 'brand'
end
end
I assume the structure of this is not right, as I am currently trying to set up URL consisting of the name (Sting) for each of the different brands, e.g: localhost:3000/store/brand1, localhost:3000/store/brand2 etc...
thanks in advance for any structural advice/guiding on how to get this right!

How to create multiple "has_many through" associations through one form?

I'm building a martial arts related database, currently I have the following associations set up:
Student has_and_belongs_to_many :styles
Style has_many :ranks
Student has_many :ranks, through: :gradings (and vice versa)
I'm generating a form as follows, depending on the student's styles:
So the headings are generated by the Style model (Tai Chi, Karate...), then their rankings listed below (taken from the Rank model), and the "Dojo" and "Date" fields should belong to the Grading model once created.
The question: I know how to build a form that creates one association (or one association + its children), but how do I build a form that creates multiple associations at once?
Also, what would be a clean way to implement the following:
Only lines which are ticked become associations
Dojo and date must be filled in for ticked lines to save successfully
If a line is unticked it will destroy any previously created associations
This is what I've currently implemented to retrieve the correct records:
class GradingsController < ApplicationController
before_filter :authenticate_sensei!
def index
#student = Student.includes(:styles).find(params[:student_id])
#ranks = Rank.for_student_styles(#student)
split_ranks_by_style
end
private
def split_ranks_by_style
#karate = #ranks.select_style("Karate")
#tai_chi = #ranks.select_style("Tai Chi")
#weaponry = #ranks.select_style("Weaponry")
end
end
# Rank model
def self.for_student_styles(student)
includes(:style).where("styles.id in (?)", student.styles.map(&:id))
end
def self.select_style(style)
all.map { |r| r if r.style.name == style }.compact
end
Complicated forms like this are best handled in a service object initiated in the primary resource's create or update action. This allows you to easily find where the logic is happening afterwards. In this case it looks like you can kick off your service object in your GradingsController. I also prefer formatting a lot of the data in the markup, to make the handling easier in the service object. This can be done a'la rails, by passing a name like "grade[style]" and "grade[rank]". This will format your params coming in as a convenient hash: {grade: {style: "karate", rank: "3"}}. That hash can be passed to your service object to be parsed through.
Without really grasping the full extent of your specific requirements, let's put together an example form:
<%= form_for :grading, url: gradings_path do |f| %>
<h1><%= #rank.name %></h1>
<%- #grades.each do |grade| %>
<div>
<%= hidden_field_tag "grade[#{grade.id}][id]", grade.id %>
<%= check_box_tag "grade[#{grade.id}][active]" %>
...
<%= text_field_tag "grade[#{grade.id}][date]" %>
</div>
<%- end %>
<%= submit_tag %>
<%- end %>
With a form like this, you get your params coming into the controller looking something like this:
"grade"=>{
"1"=>{"id"=>"1", "active"=>"1", "date"=>"2013-06-21"},
"3"=>{"id"=>"3", "date"=>"2013-07-01"}
}
Nicely formatted for us to hand off to our service object. Keeping our controller nice and clean:
class GradingsController < ApplicationController
def index
# ...
end
def create
builder = GradeBuilder.new(current_user, params['grade'])
if builder.run
redirect_to gradings_path
else
flash[:error] = 'Something went wrong!' # maybe even builder.error_message
render :action => :index
end
end
end
So now we just need to put any custom logic into our builder, I'd probably recommend just making a simple ruby class in your /lib directory. It could look something like this:
class GradeBuilder
attr_reader :data, :user
def self.initialize(user, params={})
#user = user
#data = params.values.select{|param| param['active'].present? }
end
def run
grades = data.each{|entry| build_grade(entry)}
return false if grades.empty?
end
private
def build_grade(entry)
grade = Grade.find(entry['id'])
rank = grade.rankings.create(student_id: user, date: entry['date'])
end
end
There will obviously need a lot more work to pass all the specific data you need from the form, and extra logic in the GradeBuilder to handle edge cases, but this will give you a framework to handle this problem in a maintainable and extensible way.

Use ActiveAdmin to edit a single record

I have a model that needs editing that is associated with the current user called BillingProfile. How can I add a menu item that links to the edit page of the current user's BillingProfile? I don't want or need an index page for the BillingProfile as a user can only edit their own.
class User
has_one :billing_profile
end
You can use Cancan to manage the ability to allow a User to edit his own Billing Profile.
ability.rb
...
cannot :edit_billing_profile, User do |u|
u.user_id != user.id
end
...
admin/users.rb
ActiveAdmin.register User do
action_item :only => :show do
link_to "Edit BP", edit_bp_path(user.id) if can? :edit_billing_profile, user
end
end
Or you can try something like this:
ActiveAdmin.register User do
form do |f|
f.inputs "User" do
f.input :name
end
f.inputs "Billing Profile" do
f.has_one :billing_profile do |bp|
w.input :address if can? :edit_billing_profile, bp.user
end
end
f.buttons
end
end
I have not test it, but I did something similar on a project.
This may help you-
Adding custom links:
ActiveAdmin.register User, :name_space => :example_namespace do
controller do
private
def current_menu
item = ActiveAdmin::MenuItem.new :label => "Link Name", :url => 'http://google.com'
ActiveAdmin.application.namespaces[:example_namespace].menu.add(item)
ActiveAdmin.application.namespaces[:example_namespace].menu
end
end
end
I basically created a new ActiveAdmin::MenuItem and add it to the current ActiveAdmin menu with the namespace example_namespace and return the menu in the end of the current_menu method. Note: current_menu is a method expected by ActiveAdmin so don't change the name of it. You can add as many items you like and each of these items will be converted to a link on your navigation header. Note this works for ActiveAdmin version > 0.4.3 so you might need to do your own digging if you want to do it for version <= 0.4.3.
I have a LinkHelper defined which has the following two methods:
#This will return an edit link for the specified object instance
def edit_path_for_object_instance(object_instance)
model_name = object_instance.class.to_s.underscore
path = send("edit_#{model_name}_path", object_instance)
end
#This will return an show link for the specified object instance
def show_path_for_object_instance(object_instance)
model_name = object_instance.class.to_s.underscore
path = send("#{model_name}_path", object_instance)
end
You can call the edit_path_for_object_instance method directly from your view and pass in the user.billing_profile object.
This will give you a link directly to the entity resulting a url like /billing_profile/ID/edit
An alternate approach is to use fields_for. This will allow you to create a form for the User attributes and update the associated BillingProfile at the same time. It would look something like this:
<%= form_for #user%>
<%= fields_for #user.billing_profile do |billing_profile_fields| %>
<%= billing_profile_fields.text_field :name %>
<% end %>
<%end%>
See here: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html

Resources