model/songs.rb
require 'file_size_validator'
class Song < ActiveRecord::Base
mount_uploader :attachment, AttachmentUploader
validates :attachment, :presence => true,
:file_size => {:maximum => 2.megabytes.to_i}
end
new.html.erb
<div class="well">
<%= form_for #song, html: { multipart: true } do |f| %>
<%= f.label :attachment %>
<%= f.file_field :attachment %>
<%= f.submit "Upload", class: "btn btn-primary" %>
<% end %>
</div>
when I submit empty form or form without file selection it gives error as :-
ActionController::ParameterMissing at /songs
param is missing or the value is empty: songs
actionpack (4.1.4) lib/action_controller/metal/strong_parameters.rb
182 def require(key)
183 self[key].presence || raise(ParameterMissing.new(key))
184 end
I'm wondering why ActiveRecords::Base validates :presence => true not working.
(new bie to rials)
songs_controller.rb
require 'zip'
require 'taglib'
class SongsController < ApplicationController
respond_to :html, :js
def index
session[:ids] = nil
#songs = Song.where("song_name IS NOT NULL AND song_path IS NOT NULL").page(1).per(5)
end
def new
#song = Song.new
#songs = Song.find(session[:ids]) if !session[:ids].nil?
end
def create
#song = Song.new(song_params)
if #song.save
puts #song.errors.messages
zip_file_path = Pathname.new(#song.attachment.current_path)
destination_path = zip_file_path.dirname
unzip(zip_file_path, destination_path, true) if zip_file_path.extname == ".zip"
session[:ids] = Array.new
Dir.glob("#{destination_path}" + '/*') do |music_files|
if %w(.mp3 .wav .aac .wma .m4a).include? File.extname(music_files)
song_details = extract_music_metadata(music_files)
if !song_details.nil?
#song = Song.new(song_details)
session[:ids].push(#song.id) if #song.save
end
else
File.delete(music_files)
end
end
redirect_to new_song_url, notice: "The contents of zip files are listed below !!!"
else
render "new"
end
end
def edit
#song = Song.find(params[:id])
end
def update
#songs = Song.where("song_name IS NOT NULL AND song_path IS NOT NULL").page(1).per(5)
#song = Song.find(params[:id])
#song.update_attributes(song_params)
if !session[:ids].nil?
#songs = Song.find(session[:ids])
end
end
def delete
#song = Song.find(params[:id])
end
def destroy
#songs = Song.where("song_name IS NOT NULL AND song_path IS NOT NULL").page(1).per(5)
#song = Song.find(params[:id])
#song.destroy
File.delete(#song.song_path)
if !session[:ids].nil?
#songs = Song.find(session[:ids])
end
end
private
def song_params
params.require(:song).permit(:attachment, :title, :artist, :album, :year, :track,
:genre, :comment)
end
# extract contents of a zip file
def unzip(zip, unzip_dir, remove_after = false)
Zip::File.open(zip) do |zip_file|
zip_file.each do |f|
f_path=File.join(unzip_dir, f.name)
FileUtils.mkdir_p(File.dirname(f_path))
a = zip_file.extract(f, f_path) unless File.exist?(f_path)
end
end
FileUtils.rm(zip) if remove_after
end
def extract_music_metadata(music_files)
TagLib::FileRef.open(music_files) do |fileref|
unless fileref.null?
tag = fileref.tag
song_details = {
song_name: File.basename(music_files),
song_path: music_files,
title: tag.title,
artist: tag.artist,
album: tag.album,
year: tag.year,
track: tag.track,
genre: tag.genre,
comment: tag.comment
}
end
end
end
end
Error from console is:
Started POST "/songs" for 127.0.0.1 at 2014-10-16 11:45:10 +0545
Processing by SongsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"oLgt6szICw1/4FIiP4dQk2IGSPqQb3l7roS0E1l7ovk=", "commit"=>"Upload"}
Completed 400 Bad Request in 1ms
ActionController::ParameterMissing - param is missing or the value is empty: song:
Related
I am working on a quiz functionality, and I'd like to show the user the results for 2s before redirecting him to the next question.
Here is my code to show the results to the user (it works):
def validate_answer
answer_given = params[:answer_given]
quiz_question_id = params[:quiz_question_id]
next_question_id = params[:next_question_id]
is_right_answer = is_right_answer(Question.find(params[:question_id]), answer_given)
#quiz_question = QuizQuestion.update(quiz_question_id,answer_given:answer_given,is_answer_right:is_right_answer)
#question = Question.find(params[:question_id])
#card = #question.card
respond_to do |format|
format.turbo_stream do
render turbo_stream: [
turbo_stream.update('questionPartialId', partial: "questionPartial"),
]
end
end
end
Now if I add the sleep & redirect it does not work as the sleeps blocks the whole app (see code below)
How should I do to perform this sleep ?
def validate_answer
answer_given = params[:answer_given]
quiz_question_id = params[:quiz_question_id]
next_question_id = params[:next_question_id]
is_right_answer = is_right_answer(Question.find(params[:question_id]), answer_given)
#quiz_question = QuizQuestion.update(quiz_question_id,answer_given:answer_given,is_answer_right:is_right_answer)
#question = Question.find(params[:question_id])
#card = #question.card
respond_to do |format|
format.turbo_stream do
render turbo_stream: [
turbo_stream.update('questionPartialId', partial: "questionPartial"),
]
end
end
sleep(2)
if next_question_id != ""
redirect_to(question_path(next_question_id))
else
redirect_to(quiz_path(QuizQuestion.find(quiz_question_id).quiz_id))
end
end
Thanks a lot in advance !
I have file, which should be updated after updating instance attributes
It looks like this:
controller.rb
def update
if UpdateNewspaperDesign.new(newspaper_design, newspaper_design_params).call
#render response
end
end
and
class UpdateNewspaperDesign
def initialize(newspaper_design, params)
#newspaper_design = newspaper_design
#params = params
end
def call
newspaper_design.assign_attributes(params)
File.open(File.join(Rails.root, 'tmp', "newspaper-design-#{Time.now.to_i}.css"), 'w') do |file|
file.write(css(newspaper_design))
newspaper_design.css = file
end
newspaper_design.save
end
private
attr_reader :newspaper_design, :params
def css(newspaper_design)
NewspaperDesignCssCompiler.new(newspaper_design).call
end
end
And then I include css in layout
= stylesheet_link_tag newspaper.design.css_url, media: 'all'
As I see in console: css url is updated, but the url, which is inserted in layout - is not update.
Replace File with Tempfile
- this is how I fixed it
How can we go from date_started until Date.current?
For example, this code only counts the date_started for a routine:
routines = current_user.routines.group_by {|i| i.date_started.to_date}
classes << "completed" if routines.include?(day)
But I'd like to make "completed", which is the CSS class that makes the td background blue, work like I explain below:
schema
create_table "routines", force: true do |t|
t.datetime "date_started"
t.string "action"
t.integer "user_id"
end
I had followed this tutorial to build the calendar.
FULL calendar_helper.rb
module CalendarHelper
def calendar(date = Date.current, &block)
Calendar.new(self, date, block).table
end
class Calendar < Struct.new(:view, :date, :callback)
HEADER = %w[Sun Mon Tue Wed Thu Fri Sat]
START_DAY = :sunday
delegate :content_tag, :current_user, to: :view
def past
end
def table
content_tag :table, class: "calendar" do
header + week_rows
end
end
def header
content_tag :tr do
HEADER.map { |day| content_tag :th, day }.join.html_safe
end
end
def week_rows
weeks.map do |week|
content_tag :tr do
week.map { |day| day_cell(day) }.join.html_safe
end
end.join.html_safe
end
def day_cell(day)
content_tag :td, view.capture(day, &callback), class: day_classes(day)
end
def day_classes(day)
classes = []
classes << "lastmonth" if day.month < date.month
classes << "nextmonth" if day.month > date.month
###
routines = current_user.routines.group_by {|i| i.date_started.to_date}
classes << "completed" if routines.include?(day)
###
classes.empty? ? nil : classes.join(" ")
end
def weeks
first = date.beginning_of_month.beginning_of_week(START_DAY)
last = date.end_of_month.end_of_week(START_DAY)
(first..last).to_a.in_groups_of(7)
end
end
end
If I'm understanding correctly why not just check if the given day in the calender falls within your specified range of dates?
controller:
#date_started = Date.today - 2.days
#today = Date.today
#given_date = Date.yesterday
html:
<td class="<%= 'completed' if #given_date >= #date_started and #given_date <= #today %>">
EDIT
I suppose #given_date will need to be an array so each day can be iterated in your HTML. It's not clear how to write that without seeing how you're rendering your calender. But the general idea should work.
EDIT2
The tutorial HTML is something like this:
<%= calender do |date| %>
<%= date.day %>
# assuming this is where you add your `completed` css class somehow
# check if date.day falls between the #date_started and #today range
<% end %>
EDIT3
Okay I see why this is confusing now. I think I understand though. You have have many routine objects and you want any date that falls between the routine.date_started and Date.today to be highlighted blue. I believe this will work for you:
def day_classes(day)
classes = []
classes << "lastmonth" if day.month < date.month
classes << "nextmonth" if day.month > date.month
###
routines = current_user.routines.group_by {|i| i.date_started.to_date}
# remove this line:
#classes << "completed" if routines.include?(day)
# in favor of this line:
classes << "completed" if routines.any? {|date_attr, routine_obj| day >= date_attr and day <= Date.today }
###
classes.empty? ? nil : classes.join(" ")
end
I have upgraded Refinery CMS to the newest version (2.1.0), where there is a new approach in rendering the navigation menu :
(in partial _header.html.erb)
<%= Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self).to_html %>
The older version of the same partial :
<%= render(:partial => "/refinery/menu", :locals => {
:dom_id => 'menu',
:css => 'menu'
}) %>
How could I add bootstrap styles to the navbar using MenuPresenter?
It can be done, but the solution is not pretty because the Menu Presenter in Refinery 2.1 doesn't support all the right CSS options out of the box. But with a bit of perseverance, this is roughly what to do:
Firstly, create a new blank file here: config/initializers/refinery/monkey_patch_menu_presenter.rb
In this patch file, paste in the contents of this updated version of the menu presenter (published October 2013): menu_presenter.rb
Next, based on the instructions in section 5 of the menu presenter guide, in your app/helpers/application_helper.rb file, add a new method called navigation_menu:
def navigation_menu
presenter = Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self)
presenter.css = "navbar-inner"
presenter.menu_tag = :div
presenter.list_tag_css = "nav"
presenter.selected_css = "active"
presenter.first_css = ""
presenter.last_css = ""
presenter.max_depth = 0 # prevents dropdown menus, which don't render correctly
presenter
end
Finally, in your app/views/refinery/_header.html.erb file (use $ bundle exec rake refinery:override view=refinery/_header if it doesn't exist), replace the call for:
<%= Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self).to_html %>
with:
<div class="navbar">
<%= navigation_menu.to_html %>
</div>
Ensure that you have the loaded the Bootstrap CSS/JS files and have wrapped the whole page in a <div class="container"> element. Then restart your application for the patch to take affect and hopefully you'll see a familiar bootstrap navigation bar.
Good luck!
Martyn.
Here a version of above menu_presenter.rb that renders sub-menus as well
(This if for Bootstrap 3, RefineryCMS 2.1.1):
require 'active_support/core_ext/string'
require 'active_support/configurable'
require 'action_view/helpers/tag_helper'
require 'action_view/helpers/url_helper'
module Refinery
module Pages
class MenuPresenter
include ActionView::Helpers::TagHelper
include ActionView::Helpers::UrlHelper
include ActiveSupport::Configurable
config_accessor :roots, :menu_tag, :list_tag, :list_item_tag, :css, :dom_id,
:max_depth, :selected_css, :first_css, :last_css, :list_tag_css,
:link_tag_css
self.dom_id = 'menu'
self.css = "collapse navbar-collapse"
self.menu_tag = :div
self.list_tag = :ul
self.list_item_tag = :li
self.selected_css = 'active'
self.first_css = :first
self.last_css = :last
self.list_tag_css = "nav navbar-nav"
def roots
config.roots.presence || collection.roots
end
attr_accessor :context, :collection
delegate :output_buffer, :output_buffer=, :to => :context
def initialize(collection, context)
#collection = collection
#context = context
end
def to_html
render_menu(roots) if roots.present?
end
private
def render_menu(items)
content_tag(menu_tag, :id => dom_id, :class => css) do
render_menu_items(items)
end
end
def render_menu_items(menu_items)
if menu_items.present?
content_tag(list_tag, :class => list_tag_css) do
menu_items.each_with_index.inject(ActiveSupport::SafeBuffer.new) do |buffer, (item, index)|
buffer << render_menu_item(item, index)
end
end
end
end
def render_menu_items_children(menu_items)
if menu_items.present?
content_tag(list_tag, :class => 'dropdown-menu') do
menu_items.each_with_index.inject(ActiveSupport::SafeBuffer.new) do |buffer, (item, index)|
buffer << render_menu_item(item, index)
end
end
end
end
def render_menu_item_link_dropdown(menu_item)
link_to( menu_item.title, context.refinery.url_for(menu_item.url), class: "dropdown-toggle", data: {toggle:"dropdown", target: "#"})
end
def render_menu_item_link(menu_item)
link_to(menu_item.title, context.refinery.url_for(menu_item.url), :class => link_tag_css)
end
def render_menu_item(menu_item, index)
content_tag(list_item_tag, :class => menu_item_css(menu_item, index)) do
buffer = ActiveSupport::SafeBuffer.new
# Check for sub menu
menu_item_children(menu_item).empty? ? buffer << render_menu_item_link(menu_item) : buffer << render_menu_item_link_dropdown(menu_item)
buffer << render_menu_items_children(menu_item_children(menu_item))
buffer
end
end
# Determines whether any item underneath the supplied item is the current item according to rails.
# Just calls selected_item? for each descendant of the supplied item
# unless it first quickly determines that there are no descendants.
def descendant_item_selected?(item)
item.has_children? && item.descendants.any?(&method(:selected_item?))
end
def selected_item_or_descendant_item_selected?(item)
selected_item?(item) || descendant_item_selected?(item)
end
# Determine whether the supplied item is the currently open item according to Refinery.
def selected_item?(item)
path = context.request.path
path = path.force_encoding('utf-8') if path.respond_to?(:force_encoding)
# Ensure we match the path without the locale, if present.
if %r{^/#{::I18n.locale}/} === path
path = path.split(%r{^/#{::I18n.locale}}).last.presence || "/"
end
# First try to match against a "menu match" value, if available.
return true if item.try(:menu_match).present? && path =~ Regexp.new(item.menu_match)
# Find the first url that is a string.
url = [item.url]
url << ['', item.url[:path]].compact.flatten.join('/') if item.url.respond_to?(:keys)
url = url.last.match(%r{^/#{::I18n.locale.to_s}(/.*)}) ? $1 : url.detect{|u| u.is_a?(String)}
# Now use all possible vectors to try to find a valid match
[path, URI.decode(path)].include?(url) || path == "/#{item.original_id}"
end
def menu_item_css(menu_item, index)
css = []
css << selected_css if selected_item_or_descendant_item_selected?(menu_item)
css << "dropdown" unless menu_item_children(menu_item).empty?
css << first_css if index == 0
css << last_css if index == menu_item.shown_siblings.length
css.reject(&:blank?).presence
end
def menu_item_children(menu_item)
within_max_depth?(menu_item) ? menu_item.children : []
end
def within_max_depth?(menu_item)
!max_depth || menu_item.depth < max_depth
end
end
end
end
Using RubyMotion (for the first time!), I want to use Twitter's search API to retrieve some recent tweets for some users so have put together the class below.
The value of tweets is always an empty array. I suspect that BW::HTTP.get(url) spawns its own thread which is causing the issue.
Really, I just want twitter_search_results to return response.body.to_str but I am not sure how to do this.
How do I use RubyMotion (or BubbleWrap) to put an array of Tweet objects into my UIViewController?
class TweetsController
def initialize
#twitter_accounts = %w(dhh google)
#tweets = []
end
def tweets
twitter_search_results
puts #tweets.count
#tweets
end
def create_tweets(response)
BW::JSON.parse(response)["results"].each do |result|
#tweets << Tweet.new(result)
end
end
def twitter_search_results
query = #twitter_accounts.map{ |account| "from:#{account}" }.join(" OR ")
url = "http://search.twitter.com/search.json?q=#{query}"
BW::HTTP.get(url) do |response|
create_tweets(response.body.to_str)
end
end
end
class TwitterViewController < UIViewController
def viewDidLoad
super
self.view.backgroundColor = UIColor.blueColor
#table = UITableView.alloc.initWithFrame(self.view.bounds)
self.view.addSubview #table
#table.dataSource = self
#tweets_controller = TweetsController.new
end
def initWithNibName(name, bundle: bundle)
super
self.tabBarItem = UITabBarItem.alloc.initWithTitle(
"Twitter",
image: UIImage.imageNamed('twitter.png'),
tag: 1)
self
end
def tableView(tableView, numberOfRowsInSection: section)
#tweets_controller.tweets.length
end
def tableView(tableView, cellForRowAtIndexPath: indexPath)
#reuse_id = "Tweet"
cell = UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:#reuse_id)
cell.textLabel.text = #tweets_controller.tweets[indexPath.row].text
return cell
end
end
class Tweet
attr_reader :created_at, :from_user, :text
def initialize(tweet_result)
#created_at = tweet_result["created_at"]
#from_user = tweet_result["from_user"]
#text = tweet_result["text"]
end
end
Full controller code below. I've also put the project on GitHub
class TweetsController
def initialize
#twitter_accounts = %w(dhh google)
#tweets = []
create_tweets
end
def tweets
#tweets
end
def create_tweets
json_data = twitter_search_results.dataUsingEncoding(NSUTF8StringEncoding)
e = Pointer.new(:object)
dict = NSJSONSerialization.JSONObjectWithData(json_data, options:0, error: e)
dict["results"].each do |result|
p result.class
p result
#tweets << Tweet.new(result)
end
end
def twitter_search_results
query = #twitter_accounts.map{ |account| "from:#{account}" }.join(" OR ")
url_string = "http://search.twitter.com/search.json?q=#{query}"
url_string_escaped = url_string.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
url = NSURL.URLWithString(url_string_escaped)
request = NSURLRequest.requestWithURL(url)
response = nil
error = nil
data = NSURLConnection.sendSynchronousRequest(request, returningResponse: response, error: error)
raise "BOOM!" unless (data.length > 0 && error.nil?)
json = NSString.alloc.initWithData(data, encoding: NSUTF8StringEncoding)
end
end
the issue here is asynchronicity. you're almost there, I think, but the create_tweets method is not called before puts #tweets. In this case, I would recommend using a notification, because I think they are good ;-)
TweetsReady = 'TweetsReady' # constants are nice
NSNotificationCenter.defaultCenter.postNotificationName(TweetsReady, object:#tweets)
In your controller, register for this notification in `viewWillAppear` and unregister in `viewWillDisappear`
NSNotificationCenter.defaultCenter.addObserver(self, selector: 'tweets_ready:', name: TweetsReady, object:nil) # object:nil means 'register for all events, not just ones associated with 'object'
# ...
NSNotificationCenter.defaultCenter.removeObserver(self, name:TweetsReady, object:nil)
and you tweets_ready method should implement your UI changes.
def tweets_ready(notification)
#table.reloadData
end