Ruby on Rails 5 Star Rating with Star Rating Widget

Description

With countless rating systems out there I decided to go with JQuery’s Star Rating Widget Plugin for one of my pet sites: www.recisphere.com. The only problem with this is a complete lack of documentation on how to go about using it. I did a dirty hack but it seems to work great so I am going to show you how to set up a rating system with ruby on rails and then use the star rating widget.

Step 1:

I won’t go through this in detail but you will need to have Rails <= 3.09 installed (possibly works in earlier versions but it's not tested) and include the most recent Jquery and plugin javascript and css files. Just follow the Star Rating Widget’s install and required documents section. Also note I use the gem ‘simple_form’, but this can easily be edited to your specific needs.

Step 2:

The plugin pretty much puts makeup on an already existing rating system. You will need to determine the best way to store your ratings and work with that data. Here I will simply show the input. The Star Rating Widget can take input from a few different types. I prefer to use radio buttons. Thus what I did was
[bash]
rails g scaffold user_rating user_id:integer recipe_id:integer user_rating:decimal
[/bash]
This gives us a scaffold and controller to work with. The next step was to edit the controller.
[ruby]
def create
@user_rating = UserRating.find_or_initialize_by_user_id_and_recipe_id(params[:user_rating])
@user_rating.update_attributes(params[:user_rating])
respond_to do |format|
if @user_rating.save
format.html { redirect_to(@user_rating,
:notice => ‘Recipe was successfully created.’) }
format.js
format.xml { render :xml => @user_rating,
:status => :created, :location => @user_rating }
else
format.html { render :action => "new" }
format.xml { render :xml => @user_rating.errors,
:status => :unprocessable_entity }
end
end
end
def edit
@user_rating = UserRating.find_by_user_id_and_recipe_id(params[:user_rating])
end

def update
@user_rating = UserRating.find_by_user_id_and_recipe_id(params[:user_rating])
respond_to do |format|
if @user_rating.update_attributes(params[:user_rating])
format.html { redirect_to(@user_rating, :notice => ‘Recipe was successfully updated.’) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @user_rating.errors, :status => :unprocessable_entity }
end
end
end
end
[/ruby]

UserRating.find_or_initialize_by_user_id_and_recipe_id(params[:user_rating]) allows us to create if it doesn’t exist and edit later if it does exist.

Step 3:

Edit wherever you want your form to be kept. The Star Rating Widget is going to change the input from 10 (or how ever many radio buttons you want) into 5 stars. This will have a hidden input that will need to get submitted. The widget has a callback function that will allow any number of functions to be called everytime a rating is changed. By using the callback function with the widget we can successfully change the rating at any time, which is similar to how Netflix operates.
[ruby]

<%= simple_form_for :user_rating, :url => user_ratings_path, :remote => true do |f| %>
<div id="stars-wrapper<%= @recipe.id.to_s %>">
<% (1…11).each do |rates| %>
<%= f.radio_button :user_rating, rates, :class => ["star", @recipe], :checked => rates == (checked)? true : false %>
<% end %>
<% if user_signed_in? %>
<%= f.input :user_id, :as => :hidden, :input_html => { :value => current_user.id } %>
<% end %>
<%= f.input :recipe_id, :as => :hidden, :input_html => { :value => @recipe.id } %>
<%= f.button :submit, :id => "starbutton" %>
</div>
<% end %>
<script type="text/javascript">
$j("<%= "#stars-wrapper" << @recipe.id.to_s %>").stars({
callback: function(value, link){
document.getElementById("starbutton").click();
}
});
</script>
[/ruby]

Step 4:

Now that we have a form submitting and storing values we need to add a little bit of ajax into this so that the page doesn’t get redirected continually. The secret to getting the ajax to work is in the format.js in the controller. Since format.js is a respond call in the create definition it will look for a create.js in the views directory.

Open “ROOTDIR”/app/views/user_ratings/create.js.rjs
[ruby]
page.replace_html(‘stars_show’, render("recipes/star_fields"))
[/ruby]
This uses the prototype library which comes with Rails. Be warned Prototype and JQuery do not work together very well. I had to make a hack to allow me to call jquery calls by making a javascript file called ‘noconflict.js’ and putting the following code in. This is why you see a $j in my javascript calls earlier.
[javascript]
jQuery.noConflict();
var $j = jQuery;
[/javascript]

Conclusion

We just made a rating system for a rails site. Made the controller which will allow us to make a form and submit data to our database. We then made sure we had a working form and covered it with some pretty stars from the Jquery Star Rating Widget Plugin. Then we added a touch of ajax to allow us to not have to redirect or refresh an entire page each time a rating is made or changed.

Facebook Access Vietnam

Description

Due to lovely government restrictions of freedom of speech in Vietnam the website www.facebook.com has now been blocked. The enforcement of this is not very strick and is rather easy to work around. The solution is to reroute the dns services coming to your computer instead of using the default ones provided by any isp in Vietnam. Always use two DNS providers, I have provided a few common DNS servers (Google and Norton), but any DNS server outside of Vietnam and China will work.

DNS Addresses

Google

  • 8.8.8.8
  • 8.8.4.4

Norton

  • 198.153.192.1
  • 198.153.194.1

Change DNS Mac OSX

[text]System Preferences> Network> Built-in Ethernet> Advanced> DNS tab> DNS Servers in Section 8.8.8.8 and 8.8.4.4 added 2 lines[/text]

System Preferences

Internet Preferences

Change Ethernet

DNS Change

Mapping the IP Addresses

Edit the /etc/hosts files in order to redirect the calls to facebook through servers outside of Vietnam.
[bash]
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##

125.252.224.88 facebook.com
125.252.224.88 www.facebook.com
69.63.181.12 apps.facebook.com
153.16.15.71 upload.facebook.com
153.16.15.71 graph.facebook.com
153.16.15.71 facebook.com
153.16.15.71 http://www.facebook.com
153.16.15.71 apps.facebook.com
153.16.15.71 login.facebook.com
153.16.15.71 graph.facebook.com
153.16.15.71 static.ak.connect.facebook.com
153.16.15.71 developers.facebook.com
153.16.15.71 error.facebook.com
153.16.15.71 upload.facebook.com
153.16.15.71 register.facebook.com
153.16.15.71 bigzipfiles.facebook.com
153.16.15.71 pixel.facebook.com

127.0.0.1 localhost
[/bash]

Now just go to www.facebook.com and everything should work.

Change DNS Windows

[text]
Go to Control Panel->Network Connections->local network->Properties->Internet Protocol (TCP/IP)->Properties.
[/text]

windows dns

Mapping the IP Addresses

This file is the same as Mac OSX but is located C:WindowsSystem32driversetchosts

Webserver for OSX Tutorial

Description

This tutorial is will show how to set up an apache server for web development which allows for sites to be written in any language and framework. This is tailored for Mac OS X and is meant for developers who write websites in multiple languages and frameworks. This tutorial starts by setting up apache, languages and framework followed by the site setup. The site setup is a website that allows browsing of web directories for viewing.

Preview for web development platform

Minimum Requirements

  • php 5.0
  • apache 2.0
  • gems 1.3

Setup

OSX and Apache Configuration

First go to System Preferences -> Sharing and click on Web Sharing which allows for apache to be turned on. The “Computer Name” will be the name entered into a web browser in order to all developing sites.

Sharing Window

Next open /etc/apache2/http.conf and edit the following lines:

DocumentRoot "/Library/WebServer/Documents"
<IfModule dir_module>
  DirectoryIndex index.html
</IfModule>

and change them to:

DocumentRoot "/Users/<username>/websites"
<IfModule dir_module>
  DirectoryIndex index.html index.cgi index.pl index.php index.xhtml
</IfModule>

My “Computer Name” is argonath, but this should be the “Computer Name” chosen earlier.

255.255.255.255	broadcasthost
::1             localhost 
fe80::1%lo0	localhost
127.0.0.1 argonath

Site Configuration

cd /Users/
git clone http://www.zaphinath.com/git/webdevosx.git websites

Then open the browser of your choice and go to the address you chose in the setting up of apache. In my example I go to argonath/ and it will load the web development platform.

Ruby on Rails Configuration

Macport and XCode Tools should both be installed and configured in order to install ruby on rails. I found I had to add a symbolic link to the mysql5 mysqld.sock file because MacPorts puts it in /opt/local and therefore it couldn’t be found at /tmp/mysql.sock.

sudo port install mysql5 +server
sudo port install ruby
sudo port install rb-rubygems
sudo port install rb-termios
sudo port install rb-mysql
sudo ln -s /opt/local/var/run/mysql5/mysqld.sock /tmp/mysql.sock

sudo gem update --system
sudo gem install -y rake
sudo gem install rails --include-dependencies
sudo gem install passenger
sudo passenger-install-apache2-module

sudo bundle install

Symfony Configuration

Go to http://symfony.com/download and download the latest version of the Symfony Framework. After the file is unpacked move it to the website directory. This directory is a blank template of the Symfony Project. For every Symfony-php project made just simply copy the Symfony directory into the directory of the new project.

mv ~/Downloads/Symfony ~/websites/Symfony/Symfony
cp ~/websites/Symfony/Symfony ~/websites/Symfony/

Coldfusion Configuration

Python Ctypes for Advanced Fortran and C++

Problem

The cytpes library in python 2.6 and greater allows python to call fortran, c, and c++ functions from a specially compiled library. However the python cytpes library is limited in the datatypes that can be called from those libraries. This tutorial will show how to call derived data types from a fortran library and structs in a c++ in python.

Ctypes Fundamental Data Types

Here is a list of data types that can be passed through the ctypes library. For more information on ctypes please visit the python cytpes tutorial.

ctypes type C type Python type
c_char char 1-character
string
c_wchar wchar_t 1-character
unicode string
c_byte char int/long
c_ubyte unsigned char int/long
c_short short int/long
c_ushort unsigned short int/long
c_int int int/long
c_uint unsigned int int/long
c_long long int/long
c_ulong unsigned long int/long
c_longlong __int64 or
long long
int/long
c_ulonglong unsigned __int64 or
unsigned long long
int/long
c_float float float
c_double double float
c_char_p char *
(NUL terminated)
string or
None
c_wchar_p wchar_t *
(NUL terminated)
unicode or
None
c_void_p void * int/long
or None

Ctypes in python does not support booleans, derived data types, objects, structs, and other commonly used data types.

Solutions

 

Derived Data Type

ddt.f90

module ddt
implicit none

type rect !Name of data type
  real :: height
  real :: length
end type rect

contains

subroutine calc_area(rc)
type(rect) :: rc
real area, h, l

h = rc%height
l = rc%length

area = h*l
print *, "The area is", area

end subroutine

end module

[bash]gfortran -fpic -c ddt.f90[/bash]

Because there is no ctype for derived data types in fortran we get around this by making an extra module in fortran that will have subroutines that we will call in python.

modtest.f90

module modtest
use ddt

contains

subroutine pcall_area
  type(rect) :: rc
  rc%height = 3.5
  rc%length = 2.5
  call calc_area(rc)
end subroutine

end module modtest

[bash]gfortran -fpic -c modtest.f90[/bash]

Now we compile a library lib.so from which our python code will call.

[bash]gfortran -fpic -shared modtest.o ddt.o -o lib.so[/bash]

Now we want to see a list of functions and subroutines we can call in our python script from the compiled library lib.so

[bash]nm lib.so | less[/bash]
[text]
0000000000000f0e T ___ddt_MOD_calc_area
0000000000000f3a T ___modtest_MOD_pcall_area
0000000000000f08 t __dyld_func_lookup
0000000000000000 t __mh_dylib_header
U dyld_stub_binder
0000000000000ef4 t dyld_stub_binding_helper
[/text]

We want to call ___modtest_MOD_pcall_area in our python script. In order to do this we need to remove one of the underscores in the front as shown:

[python firstline=”1″]
#!/usr/bin/python
#Filename: foobar.py
#Author: Derek Carr
#Website: http://www.zaphinath.com

from ctypes import *

libtest = cdll.LoadLibrary("./lib.so")

method = libtest.__modtest_MOD_pcall_area

try:
method()
print "Passed"
except:
print "Failed"
traceback.print_exc()
sys.exit()
[/python]

[bash]
chmod 777 foobar.py
./foobar.py
The area is 8.7500000
Passed
[/bash]

Now we have successfully called a subroutine from fortran that uses a derived data type or defined data type and used that subroutine in python.

Struct in C++

Line Wrap With SyntaxHighlight

Problem:

Syntax Highlighter has a problem where long lines of code use a horizontal scroll bar instead of using a line break or word wrap.

Example Before:

[bash wraplines=”false”]sql_query = SELECT page_id, page_title, page_namespace, page_is_redirect, old_id, old_text FROM vn_page, vn_revision, vn_text WHERE rev_id=page_latest AND old_id=rev_text_id[/bash]

Solution:

If using wordpress install a plugin called ‘SyntaxHighlighter Evolved’ and after the plugin is activated go to the settings toolbar and select “SyntaxHighlighter.” Note: You must be using version 2.x of SyntaxHighlighter in order to have line wrapping work.

SyntaxHighlighter Options

Example After:

[bash]sql_query = SELECT page_id, page_title, page_namespace, page_is_redirect, old_id, old_text FROM vn_page, vn_revision, vn_text WHERE rev_id=page_latest AND old_id=rev_text_id[/bash]

Spinx.conf for mediawiki

Problem:

[bash]ERROR: index ‘wiki_main’: sql_query: Table ‘db.page’ doesn’t exist (DSN=mysql://:***@localhost:3306/db)’[/bash]
[bash]ERROR: index ‘wiki_incremental’: sql_query: Table ‘db.page’ doesn’t exist (DSN=mysql://:***@localhost:3306/db)[/bash]

Solution:

This comes because in the database the tables being queried have a different name. The name was changed at the creation of the database. An example is the normal table for wiki is ‘page’ but now its ‘your_defined_prefix_page’. You will need to edit the sphinx.conf to call the right database tables. This should be fairly easy.

Before

[bash]# main document fetch query – change the table names if you are using a prefix
sql_query = SELECT page_id, page_title, page_namespace, page_is_redirect, old_id, old_text FROM page, revision, text WHERE rev_id=page_latest AND old_id=rev_text_id[/bash]

After

[bash]# main document fetch query – change the table names if you are using a prefix
sql_query = SELECT page_id, page_title, page_namespace, page_is_redirect, old_id, old_text FROM vn_page, vn_revision, vn_text WHERE rev_id=page_latest AND old_id=rev_text_id[/bash]