How to use date time precision gem - ruby-on-rails

I'm trying to allow a user to input a date in a very permissive format (e.g. year only, year & month, or exact date). The date_time_precision gem seems perfect, but I'm a newbie and I can't figure out (beyond installing the gem) how to get my form to allow these various levels of precision (I've read the readme several times).
I'm using the simple_form gem with the following in the date portion of my _form:
<%= f.input :date,
:include_blank => true,
as: :string,
order: [ :year, :month, :day,],
hint: 'Order: Year, month, date' %>
The documentation has a heading called 'Usage', and then...
require 'date_time_precision'
But I can't figure out where to put that. Does it go somewhere in my controller?
Further info:
I did all the usual stuff (added gem to gemfile, bundle install, restart server, and refresh form page).
Updates:
My form seems pretty much broken now, I keep getting errors like "ActionController::ActionControllerError in ArtworksController#update
Cannot redirect to nil!"
My routes.rb file
Rails.application.routes.draw do
root "pages#home"
get "artworks" => "artworks#index"
# post "artworks" => "artworks#index"
resources :artworks #, path: ''
get "about" => "pages#about"
end
My controller:
class ArtworksController < ApplicationController
before_action :set_artwork, only: [:show, :edit, :update, :destroy]
# GET /artworks
# GET /artworks.json
def index
#artworks = Artwork.all
end
# GET /artworks/1
# GET /artworks/1.json
def show
#artwork = Artwork.friendly.find(params[:id])
if request.path != artwork_path(#artwork)
redirect_to #artwork, status: :moved_permanently
end
end
# GET /artworks/new
def new
#artwork = Artwork.new
end
# GET /artworks/1/edit
def edit
end
# POST /artworks
# POST /artworks.json
def create
#artwork = Artwork.new( artwork_params )
respond_to do |format|
if #artwork.save
format.html { redirect_to #artwork, notice: 'Artwork was successfully created.' }
format.json { render :show, status: :created, location: #artwork }
else
format.html { render :new }
format.json { render json: #artwork.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /artworks/1
# PATCH/PUT /artworks/1.json
def update
respond_to do |format|
if #artwork.update(artwork_params)
format.html { redirect_to #artworks, notice: 'Artwork was successfully updated.' }
format.json { render :show, status: :ok, location: #artwork }
else
format.html { render :edit }
format.json { render json: #artwork.errors, status: :unprocessable_entity }
end
end
end
# DELETE /artworks/1
# DELETE /artworks/1.json
def destroy
#artwork.destroy
respond_to do |format|
format.html { redirect_to artworks_url, notice: 'Artwork was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_artwork
#artwork = Artwork.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def artwork_params
params.require(:artwork).permit(:title, :image, :genre, :category, :medium, :slug, :availability, :date, :height, :width)
end
end

Sometimes it is desirable to manipulate dates or times for which incomplete information is known. For example, one might only know the year, or the year and the month. Unfortunately, Ruby's built-in Date, Time, and Date Time classes do not keep track of the precision of the data.
In above regard, the date_time_precision gem is used. Just to provide your helper methods to complete the date/time objects.
Your Case :
In your case in your view you need a date picker / time date picker(for a better use experience).
I would suggest go with bootstrap-datepicker-rails gem
Instructions are clearly stated in the github link :
1. Install Gem.
2. Mark the related js files required in your application.js file.
3. Add your datepicker / datetimepicker to a specific class (in document ready block)
4. Use the datetimepicker in your View.

The date_time_precision library extends Ruby's Date and Time classes to have a precision attribute which tracks if only the year is known, only the year and month, or the year, month, and day.
First, install the library as a gem with this:
gem install date_time_precision
Then, this code should work for you:
require 'date_time_precision'
def Date.parse_fuzzy(str)
if /\A\s*(\d{4})\s*\z/ =~ str
Date.new($1.to_i)
else
Date.parse(str)
end
end
That's just an extension of Date.parse that can also accept standalone years. Enjoy!

Related

Redirect to a post with specific name from user input in Ruby on Rails?

I am a newbie in RoR, thus sorry for stupid question :(
I have a Game model, with a code string. There is a welcome/index view in my app with a simple form_to input. I wish to redirect user to a Game with a specific code after he submits the form.
I understand that I should somehow combine a .where method and redirect_to in Welcome_controller, but I just can't figure out how...
Welcome_controller.rb:
class WelcomeController < ApplicationController
def index
end
def redirect
redirect_to ?game with a code that equals :param from input?
end
end
Welcome/index:
<h1>Let's join the game!</h1>
<%= form_tag redirect_path do %>
<%= text_field_tag(:param) %>
<%= submit_tag("Search") %>
<% end %>
routes.rb:
Rails.application.routes.draw do
get 'welcome/index'
resources :games
get 'games/index'
root 'welcome#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
game.rb:
class Game < ApplicationRecord
validates :name, :presence => true
end
games_controller:
PREFACE = ('A'..'Z').to_a << ?_
SUFFIX = ('0'..'9').to_a
PREFACE_SIZE = 2
SUFFIX_SIZE = 3
class GamesController < ApplicationController
before_action :set_game, only: %i[ show edit update destroy ]
# GET /games or /games.json
def index
#games = Game.all
end
# GET /games/1 or /games/1.json
def show
end
# GET /games/new
def new
#game = Game.new
#game.code = gen_name
end
def gen_name
PREFACE.sample(PREFACE_SIZE).join << SUFFIX.sample(SUFFIX_SIZE).join
end
# GET /games/1/edit
def edit
end
# POST /games or /games.json
def create
#game = Game.new(game_params)
respond_to do |format|
if #game.save
format.html { redirect_to game_url(#game), notice: "Game was successfully created." }
format.json { render :show, status: :created, location: #game }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /games/1 or /games/1.json
def update
respond_to do |format|
if #game.update(game_params)
format.html { redirect_to game_url(#game), notice: "Game was successfully updated." }
format.json { render :show, status: :ok, location: #game }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
# DELETE /games/1 or /games/1.json
def destroy
#game.destroy
respond_to do |format|
format.html { redirect_to games_url, notice: "Game was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_game
#game = Game.find(params[:id])
end
# Only allow a list of trusted parameters through.
def game_params
params.require(:game).permit(:code, :name)
end
end
In config/routes.rb you have defined resources :games, which creates default paths for CRUD actions. For the show action, which you are trying to get here, it would lead to /games/:id and the helper method would be game_path. You can also check this by running rails routes -c games command in the app directory. It should return all paths for games_controller
In the before_action callback for GamesController#show action, you are finding a Game object using Game.find(params[:id]). :id parameter is what you need to pass to the path helper that I mentioned earlier for the action to fire properly, so the path to a specific game would look like game_path(id: game.id). This will then automatically get converted to params. Alternatively, you can just pass the game object to the path helper and it will do the job for you like this: game_path(game)
Now in the WelcomeController#redirect action, you get the game code in params from the form submit. You need to first find the game for the submitted code like this:
game = Game.find_by(code: params[:param])
This should work if the code is unique for each game. Now that you have the correct game record, all you need is to redirect to the path that I've mentioned eariler:
redirect_to game_path(game)

Rails 5 is ignoring view templates

In my Rails project, I ran:
rails generate scaffold car
which successfully created all of the necessary files and directories for the application. This includes the standard application.html.erb layout file, as well as the app/views/cars directory that includes more standard html.erb files (index, show, ETC.)
In addition, my cars route is in place in config/routes.rb.
My next command is rails server, which is also successful.
When I open my browser and go to localhost:3000, the welcome page for ruby on rails pops up as normal. I'm expecting that when I go to localhost:3000/cars, I'll see the html rendered from app/views/cars/index.html.erb. If not, I at least expect that it will default to application.html.erb.
Instead, I am repeatedly receiving the following:
>ActionController::UnknownFormat in CarsController#index
>CarsController#index is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: []
My controller class looks like this:
class CarsController < ApplicationController
before_action :set_car, only: [:show, :edit, :update, :destroy]
# GET /cars
# GET /cars.json
def index
#cars = Car.all
end
# GET /cars/1
# GET /cars/1.json
def show
end
# GET /cars/new
def new
#car = Car.new
end
# GET /cars/1/edit
def edit
end
# POST /cars
# POST /cars.json
def create
#car = Car.new(car_params)
respond_to do |format|
if #car.save
format.html { redirect_to #car, notice: 'Car was successfully created.' }
format.json { render :show, status: :created, location: #car }
else
format.html { render :new }
format.json { render json: #car.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /cars/1
# PATCH/PUT /cars/1.json
def update
respond_to do |format|
if #car.update(car_params)
format.html { redirect_to #car, notice: 'Car was successfully updated.' }
format.json { render :show, status: :ok, location: #car }
else
format.html { render :edit }
format.json { render json: #car.errors, status: :unprocessable_entity }
end
end
end
# DELETE /cars/1
# DELETE /cars/1.json
def destroy
#car.destroy
respond_to do |format|
format.html { redirect_to cars_url, notice: 'Car was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_car
#car = Car.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def car_params
params.fetch(:car, {})
end
end
I'd greatly appreciate any insight into this issue. I've followed every bit of advice I've seen on the internet regarding layouts, controllers, and views, and I'm having no luck.
Thanks in advance!
Thanks to everyone who was trying to answer my question! It was a stumper, but I've discovered the problem:
After uninstalling and reinstalling rails a few times, I created a dummy rails project and generated a new scaffold "car." Upon running rails server and loading up localhost:3000/cars, the default layout was rendered as normal. Thinking that reinstalling rails had solved it, I then linked the dummy project to a remote github repository.
At this point, running rails server and loading localhost:3000/cars showed me the same error message as before. This made me believe that something about the remote repo was messing up my project, so I googled around for this error message and github.
I stumbled upon the following link which ultimately answered my question:
https://github.com/rails/rails/issues/18660
Turns out, one of the directories in my root path was capitalized, and that was throwing rails off right away. As soon as I renamed the capitalized directory to all lowercase, the app ran flawlessly.
I'm frustrated by ruby on rails' extremely opinionated conventions, and especially since this project is almost a week late due to such an esoteric mistake. But I'm over the moon to have it running smoothly now and I hope this may help someone else in the future.
Thanks again all!

param is missing or the value is empty: module_list

I'm trying to enter a list of new modules and when I press 'new module list' which should take me to the form to fill out it throws up the error from the title. The application trace points at the bottom, the code inside 'def module_list_params' and also just above it where 'def set_student' is. I have no idea why it's doing it. I'm using ruby on rails.
class ModuleListsController < ApplicationController
before_action :set_module_list, only: [:show, :edit, :update, :destroy]
before_action :set_student, only: [:new, :create]
# GET /module_lists
# GET /module_lists.json
def index
#module_lists = ModuleList.all
end
# GET /module_lists/1
# GET /module_lists/1.json
def show
end
# GET /module_lists/new
def new
#module_list = #student.module_lists.new
end
# GET /module_lists/1/edit
def edit
end
# POST /module_lists
# POST /module_lists.json
def create
#module_list = #student.module_lists.new(module_list_params)
respond_to do |format|
if #module_list.save
format.html { redirect_to #module_list, notice: 'Module successfully created.' }
format.json { render :show, status: :created, location: #module_list }
else
format.html { render :new }
format.json { render json: #module_list.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /module_lists/1
# PATCH/PUT /module_lists/1.json
def update
respond_to do |format|
if #module_list.update(module_list_params)
format.html { redirect_to #module_list, notice: 'Module list was successfully updated.' }
format.json { render :show, status: :ok, location: #module_list }
else
format.html { render :edit }
format.json { render json: #module_list.errors, status: :unprocessable_entity }
end
end
end
# DELETE /module_lists/1
# DELETE /module_lists/1.json
def destroy
#module_list.destroy
respond_to do |format|
format.html { redirect_to module_lists_url, notice: 'Module list was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_module_list
#module_list = ModuleList.find(params[:id])
end
def module_list_params
params.require(:module_list).permit(:student_id, :title, :description, :credit_value)
end
def set_student
#student = Student.find_by(id: params[:student_id]) ||
Student.find(module_list_params[:student_id])
end
end
Rake routes screenshot
I believe your issue is the line before_action :set_student, only: [:new, :create]. set_student is being run when you go to the page with the form, but since there is no student_id included in the URL, it can't find anything to set it to.
To create a dependent object, there are two main ways: you can either have a form page tied to a specific parent object already, ie /students/4/module_lists/new, in which case submitting the form will create a module list tied to the student with an ID of 4. The other way is to have a general form not tied to any specific parent object, with some way of selecting a parent inside the form, eg a select or something. In that case the url would just be something like /module_lists/new.
If you want to go the first route, you'll want to nest the resources :module_lists inside of students. Check out the docs for how to do that, but it would basically look like
resources :students do
resources :module_list
end
And then in the link_to you click to go to that page, you'll need to pass in the student_id:
link_to 'Create Module List', new_student_module_list_path(#student)
For the second option, you can just remove :new from the before_action, change the new method to
def new
#module_list = ModuleList.new
end
And then add a way of picking which student to tie it to to the form.

Getting the ID of a Model that Uses Nested Routes and Permalinks in Rails

So I had my app set up with ids like so:
resources :studios do
resources :bookings
end
This gave me the route to the index (which later I'm going to use json for to get calendars for each studio.
studio_bookings GET /studios/:studio_id/bookings(.:format) bookings#index
This is good, but I wanted to get rid of the ID and use a permalink instead, just for a friendlier URL.
Change to:
namespace :studio, :path =>'/:permalink' do
resources :bookings
end
Now I'm getting
studio_bookings GET /:permalink/bookings(.:format) studio/bookings#index
Great! this is how I want my url to look, however, now the :id isn't anywhere in the route so... I get
Couldn't find Booking without an ID
It isn't even being passed. Is there a way to pass the :id in with the url without it being actually USED in the url? Otherwise, do I change the primary key from :id to :permalink in order to fix this?
I tried changing my controller from
#studio = Studio.find(params[:id])
to
#studio = Studio.find(params[:permalink])
but that gives me
Couldn't find Booking with 'id'=40frost
Which tells me what I'm doing isn't really meant to be done? It's trying to put the permalink as the id, so even though I'm telling rails to look for the permalink, it's still seemingly looking it up as an ID.
Hopefully my problem is clear: essentially - how can I pass the id so it knows which studio without displaying it in the URL. If there's some controller magic I can do instead that would be convenient.
Here's my controller for good measure
class Studio::BookingsController < ApplicationController
before_action :set_booking, only: [:show, :edit, :update, :destroy]
# GET /bookings
# GET /bookings.json
def index
#studio = Studio.find(params[:permalink])
#bookings = Booking.where("studio_id => '#studio.id'")
end
# GET /bookings/1
# GET /bookings/1.json
def show
end
# GET /bookings/new
def new
#booking = Booking.new
end
# GET /bookings/1/edit
def edit
end
# POST /bookings
# POST /bookings.json
def create
#booking = Booking.new(booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to #booking, notice: 'Booking was successfully created.' }
format.json { render action: 'show', status: :created, location: #booking }
else
format.html { render action: 'new' }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /bookings/1
# PATCH/PUT /bookings/1.json
def update
respond_to do |format|
if #booking.update(booking_params)
format.html { redirect_to #booking, notice: 'Booking was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
# DELETE /bookings/1
# DELETE /bookings/1.json
def destroy
#booking.destroy
respond_to do |format|
format.html { redirect_to bookings_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_booking
#booking = Booking.find(params[:permalink])
end
# Never trust parameters from the scary internet, only allow the white list through.
def booking_params
params.require(:booking).permit(:start_time, :end_time, :studio_id, :engineer_id, :title, :allDay)
end
end
You could just do
self.primary_key = 'permalink'
in your Studio model, or you could do
def index
#studio = Studio.find_by permalink: params[:permalink]
#bookings = Booking.where(studio_id: #studio.id)
end
depends if you just want to locally change the behavior or adress the Studio model by permalink always.
Hope that helps!

How to get name of model linked from another model in Rails?

I have set up 2 models in Rails:
class Category < ActiveRecord::Base
attr_accessible :name
has_many :platforms
end
and
class Platform < ActiveRecord::Base
attr_accessible :name, :url, :country
validates :name, :presence => true, :length => { :minimum => 5 }
validates :url, :presence => true, :length => { :minimum => 5 }
belongs_to :categories
end
This is my platform controller :
class PlatformsController < ApplicationController
# GET /platforms
# GET /platforms.json
def index
#platforms = Platform.all
#categories = Category.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #platforms }
end
end
# GET /platforms/1
# GET /platforms/1.json
def show
#platform = Platform.find(params[:id])
#categories = Platform.categories
respond_to do |format|
format.html # show.html.erb
format.json { render json: #platform }
end
end
# GET /platforms/new
# GET /platforms/new.json
def new
#platform = Platform.new
#categories = Category.all
respond_to do |format|
format.html # new.html.erb
format.json { render json: #platform }
end
end
# GET /platforms/1/edit
def edit
#platform = Platform.find(params[:id])
#categories = Category.find(:all)
end
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
##categories = Category.new(params[:name])
#categories = #platform.categories.create(params[:categories])
respond_to do |format|
if #platform.save
format.html { redirect_to #platform, notice: 'Platform was successfully created.' }
format.json { render json: #platform, status: :created, location: #platform }
else
format.html { render action: "new" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
# PUT /platforms/1
# PUT /platforms/1.json
def update
#platform = Platform.find(params[:id])
#categories = Category.find(:all)
respond_to do |format|
if #platform.update_attributes(params[:platform])
format.html { redirect_to #platform, notice: 'Platform was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
# DELETE /platforms/1
# DELETE /platforms/1.json
def destroy
#platform = Platform.find(params[:id])
#platform.destroy
respond_to do |format|
format.html { redirect_to platforms_url }
format.json { head :no_content }
end
end
end
I do not understand what I do wrong, but it doesnt correctly assign categories to platforms, and also in the platforms index view, when I try to use :
<%= platform.categories %>
it gives me error cannot find Category with id= "and here the respective id"
I am really confused since I followed tutorial for this one.
I use Rails 3.2.8
Without your view, I can't say for sure what it is you're trying to do exactly. Most importantly, what is in your params[:categories] hash? Given the name, it sounds like you intended for it to be multiple categories. However, your code is written as if you intended it to be a single set of attributes which describe one Category.
Since I can't say for sure what you want to do, I'll answer your question by explaining what you are doing. Maybe that will help you figure out how to fix it.
Your create code currently looks like this:
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
##categories = Category.new(params[:name])
#categories = #platform.categories.create(params[:categories])
The first line creates the new Platform and is easy. Skipping over the comment to the third line. This is probably what's tripping you up.
You are selecting the associations for your newly created Platform and trying to create a new category with attributes as stored in the params[:categories] hash. I'm afraid this is not allowed. (I think it throws an ActiveRecord::RecordNotSaved exception, but I could be wrong.) You can not create on a #platform which hasn't been persisted yet. Instead, I think you want build.
Here is the relevant documentation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
The difference between create and build is that build just sets up the association without actually saving it to the database yet. create saves it immediately. The nice thing about build is that you don't actually have to save it yourself. It tags along for free when you call #platform.save or #platform.update_attributes. Also, save is automatically wrapped in a transaction, so it won't create the new Category if it fails to create the new Platform for whatever reason.
The next interesting thing is that you are assigning the result of your create to #categories. I don't think this is what you want either. You don't need to save the new Category because it tags along with your #platform. However, if the save of the platform fails, then you are going to re-render your new view with this value of #categories whereas in new you set #categories = Category.all. This could certainly cause some confusion on the new view after a failed create.
In summary, I think your create code should look something like the following.
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
#platform.categories.build(params[:categories])
respond_to do |format|
if #platform.save
format.html { redirect_to #platform, notice: 'Platform was successfully created.' }
format.json { render json: #platform, status: :created, location: #platform }
else
#categories = Category.all
format.html { render action: "new" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
If you're params[:categories] is not a hash of category attributes and is actually a comma delimited string of category names, then you would want to do something like the following instead of my second line above:
params[:categories].split(",").each do |category|
#project.categories.build(name: category)
end
You may also want to check out accepts_nested_attributes_for which can DRY out your controller even more.
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I hope that helps.

Resources