Monthly Archives: December 2012

WARN Could not determine content-length of response body – Ruby on Rails

Hey everyone

I was going through my development logs today and noticed that the following line was appearing everywhere:

WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true

While nothing appeared to be wrong, it made the logs a lot harder to read. A stackoverflow post indicates that this is a webrick issue that can be avoided by switching to thin. This is also the recommended option for heroku. In order to use thin, you simply need to add it to your Gemfile:

1
2
3
4
5
#Gemfile
 
#Added gems
gem 'therubyracer' #JavaScript
gem 'thin' #Instead of webrick

Run bundle install

chris@chris-VirtualBox:~/calendar$ bundle install
Fetching gem metadata from https://rubygems.org/...........
Fetching gem metadata from https://rubygems.org/..
Using rake (10.0.2) 
Using i18n (0.6.1) 
Using multi_json (1.4.0) 
Using activesupport (3.2.9) 
Using builder (3.0.4) 
Using activemodel (3.2.9) 
Using erubis (2.7.0) 
Using journey (1.0.4) 
Using rack (1.4.1) 
Using rack-cache (1.2) 
Using rack-test (0.6.2) 
Using hike (1.2.1) 
Using tilt (1.3.3) 
Using sprockets (2.2.2) 
Using actionpack (3.2.9) 
Using mime-types (1.19) 
Using polyglot (0.3.3) 
Using treetop (1.4.12) 
Using mail (2.4.4) 
Using actionmailer (3.2.9) 
Using arel (3.0.2) 
Using tzinfo (0.3.35) 
Using activerecord (3.2.9) 
Using activeresource (3.2.9) 
Using bcrypt-ruby (3.0.1) 
Using bundler (1.2.3) 
Using coffee-script-source (1.4.0) 
Using execjs (1.4.0) 
Using coffee-script (2.2.0) 
Using rack-ssl (1.3.2) 
Using json (1.7.5) 
Using rdoc (3.12) 
Using thor (0.16.0) 
Using railties (3.2.9) 
Using coffee-rails (3.2.2) 
Installing daemons (1.1.9) 
Installing eventmachine (1.0.0) with native extensions 
Using jquery-rails (2.1.4) 
Using libv8 (3.3.10.4) 
Using pg (0.14.1) 
Using rails (3.2.9) 
Using sass (3.2.3) 
Using sass-rails (3.2.5) 
Using therubyracer (0.10.2) 
Installing thin (1.5.0) with native extensions 
Using uglifier (1.3.0) 
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

And finally start thin:

chris@chris-VirtualBox:~/calendar$ rails s thin
=> Booting Thin
=> Rails 3.2.9 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:3000, CTRL+C to stop

If you’d prefer to keep using Webrick, the following patch is said to resolve the issue:
https://bugs.ruby-lang.org/attachments/2300/204_304_keep_alive.patch

Events Only Showing in Month View – FullCalendar

Hey everyone,

Ran into a bit of a problem with FullCalendar today after setting up a JSON feed. My events were only appearing in the month view. This StackOverflow post explains that this is due to the fact that the allDay property wasn’t set.

Original JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[
    "0",
    {
        "title": "Test event",
        "id": "821",
        "start": "2012-11-23 14:00:00",
        "end": "2012-11-23 15:00:00"        
    },
    "1",
    {
        "title": "Test event 2",
        "id": "822",        
        "start": "2012-11-23 9:00:00",
        "end": "2012-11-23 10:00:00"
    },
    "2",
    {
        "title": "Test event 3",
        "id": "823",        
        "start": "2012-11-24 8:00:00",
        "end": "2012-11-24 6:00:00"
    },
    "3",
    {
        "title": "Test event 4",
        "id": "824",        
        "start": "2012-11-27 6:00:00",
        "end": "2012-11-27 7:00:00"
    }
]

Working JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[
    "0",
    {
        "allDay": "",
        "title": "Test event",
        "id": "821",
        "start": "2012-11-23 14:00:00",
        "end": "2012-11-23 15:00:00"        
    },
    "1",
    {
        "allDay": "",
        "title": "Test event 2",
        "id": "822",        
        "start": "2012-11-23 9:00:00",
        "end": "2012-11-23 10:00:00"
    },
    "2",
    {
        "allDay": "",
        "title": "Test event 3",
        "id": "823",        
        "start": "2012-11-24 8:00:00",
        "end": "2012-11-24 6:00:00"
    },
    "3",
    {
        "allDay": "",
        "title": "Test event 4",
        "id": "824",        
        "start": "2012-11-27 6:00:00",
        "end": "2012-11-27 7:00:00"
    }
]

The Name is Either Already Used or Reserved by Ruby on Rails

Hey everyone,

I ran into the following error while attempting to create a new model called calendar today:

chris@chris-VirtualBox:~/calendar$ rails g model calendar
      invoke  active_record
      The name 'Calendar' is either already used in your application or reserved by Ruby on Rails. Please choose an alternative and run this generator again.

It turns out that this was due to the fact that my application was also called Calendar. In order to get around this I decided to rename the application. This required a little bit of searching and thanks to this Stackoverflow post I found references in the following files:

– config/application.rb
– config/environment.rb
– config/environments/development.rb
– config/environments/production.rb
– config/environments/test.rb
– config/initializers/secret_token.rb
– config/initializers/session_store.rb
– config/mongoid.yml (if using Mongoid)
– config/routes.rb
– config.ru
– Rakefile
– app/views/layouts/application.html.erb, in title tag
– initializers/secret_token.rb
– initializers/session_store.rb

Let me know if you find any more spots that I’ve missed!

Refresh Folder Hotkey – Sublime Text 2

Hey everyone,

Just a quick post on how to setup a refresh hotkey in Sublime on Windows. All you have to do is the following:

1) Open sublime
2) Select Preferences from the top menu and then click “Key Bindings – Default”
3) Go to the end of the file and add a comma to the last entry:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        //Original
	{ "keys": ["enter"], "command": "hide_panel", "context":
		[{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
	},
	{ "keys": ["shift+enter"], "command": "find_prev", "context":
		[{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
	},
	{ "keys": ["alt+enter"], "command": "find_all", "args": {"close_panel": true},
		"context": [{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
	}
 
        //Modified
        //Original
	{ "keys": ["enter"], "command": "hide_panel", "context":
		[{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
	},
	{ "keys": ["shift+enter"], "command": "find_prev", "context":
		[{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
	},
	{ "keys": ["alt+enter"], "command": "find_all", "args": {"close_panel": true},
		"context": [{"key": "panel", "operand": "incremental_find"}, {"key": "panel_has_focus"}]
	}

4) Add the following entry:

1
2
//Custom
{ "keys": ["f5"], "command": "refresh_folder_list" }

Now all you have to do is save the file and F5 will now refresh your project! If you want to change the hotkey, simply switch [“f5”] for another value.

Leave a comment below if you have any issues or tips!

Full Calendar Example with Client Side Edits

FullCalendar Example with Client-Side Event Updates

Hey everyone,

I’ve been mucking around with FullCalendar recently and decided to share one of the prototypes I’ve ended up with. It basically lets the user change the events without having to do a postback. A user simply has to click the event, type in the changes and hit update.

I’ve posted the code below, however there’s also a zip which is a little easier to manage. Note that you’ll probably want to clean it up a little if you plan to use it in production. The following libraries and plugins are also used:
jQuery
jQuery UI
jQuery FullCalendar
jQuery miniColors

What it Looks Like:

Full Calendar Example with Client Side Edits

Full Calendar Example with Client Side Edits

How to Use It:

It’s all pretty straight forward, but just in case any one runs into issues there are two parts to this example. First, you generate an event template. You can then drag and drop this template onto the calendar as many times as you want.

The second part allows you to edit existing events without posting back to the server. To do this, simply click an event and then make the necessary adjustments using the top panel on the right. Once you’re done, just press update event.

The example uses the standard title property, but also includes a few others: descriptions, price, available. You can change/remove these to suit your needs, just remember to pull them out of the JavaScript as well.

Zip:

Full Calendar Zip

Basic Example Page:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<html>
	<head>
 
		<!-- Stylesheets etc -->
		<link rel="stylesheet" type="text/css" href="css/jquery-ui-1.9.2.custom.css" />
		<link rel="stylesheet" type="text/css" href="css/fullcalendar.css" />
		<link rel="stylesheet" type="text/css" href="css/view_calendar.css" />
		<link rel="stylesheet" type="text/css" href="css/jquery.miniColors.css" />
	</head>
	<body>
 
		<!-- Content Wrapper -->
		<div id="content_wrapper">
 
			<!-- Calendar div -->
			<div id="calendar">
			</div>
 
			<!-- Event generation -->
			<div id="event_generation_wrapper">
				<div class='left'>
					<div class='text'>Background:</div><br /> 	
					<div class='text'>Border:</div><br />
					<div class='text'>Text:</div><br />	
				</div>
				<div class='right'>
					<input id="txt_background_color" type='hidden' class='color_picker' value='#2795C3' /><br />
					<input id="txt_border_color" type='hidden' class='color_picker' value='#6AB3D3' /><br />
					<input id="txt_text_color" type='hidden' class='color_picker' value='#ffffff' /><br />
				</div>
				<input id='txt_title' type='text' value='Title' /><br />
				<textarea id='txt_description'>Description</textarea><br />
				<input id='txt_price' type='text' value='5.00' /><br />
				<input id='txt_available' type='text' value='5' /><br />
				<input id="btn_gen_event" type="button" value="New Template" class='btn' />
				<input id="btn_update_event" type="button" value="Update Event" class='btn'/>
				<input id="txt_current_event" type="hidden" value="" />
			</div>
 
			<!-- Booking types list -->
			<div id='external_events'>
				<div id='external_event_template' class='external-event ui-draggable'>One Hour</div>
			</div>
		</div>
 
		<!-- Include scripts at bottom to aid dom loading and prevent hangs -->
		<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js'></script>
		<script type='text/javascript' src='js/jquery-ui-1.9.2.custom.min.js'></script>
		<script type='text/javascript' src='js/fullcalendar.js'></script>
		<script type='text/javascript' src='js/view_calendar.js'></script>
		<script type='text/javascript' src='js/jquery.miniColors.js'></script>
	</body>
</html>

Basic JavaScript:

/* Wait for DOM to load etc */
$(document).ready(function(){
 
	//Initialisations
	initialise_calendar();
	initialise_color_pickers();
	initialise_buttons();
	initialise_event_generation();
	initialise_update_event();
});
 
 
/* Initialise buttons */
function initialise_buttons(){
 
	$('.btn').button();
}
 
 
/* Binds and initialises event generation functionality */
function initialise_event_generation(){
 
	//Bind event
	$('#btn_gen_event').bind('click', function(){
 
		//Retrieve template event
		var template_event = $('#external_event_template').clone();
		var background_color = $('#txt_background_color').val();
		var border_color = $('#txt_border_color').val();
		var text_color = $('#txt_text_color').val();
		var title = $('#txt_title').val();
		var description = $('#txt_description').val();
		var price = $('#txt_price').val(); 
		var available = $('#txt_available').val();
 
		//Edit id
		$(template_event).attr('id', get_uni_id());
 
		//Add template data attributes
		$(template_event).attr('data-background', background_color);
		$(template_event).attr('data-border', border_color);
		$(template_event).attr('data-text', text_color);
		$(template_event).attr('data-title', title);
		$(template_event).attr('data-description', description);
		$(template_event).attr('data-price', price);
		$(template_event).attr('data-available', available);
 
		//Style external event
		$(template_event).css('background-color', background_color);
		$(template_event).css('border-color', border_color);
		$(template_event).css('color', text_color);
 
		//Set text of external event
		$(template_event).text(title);
 
		//Append to external events container
		$('#external_events').append(template_event);
 
		//Initialise external event
		initialise_external_event('#' + $(template_event).attr('id'));
 
		//Show
		$(template_event).fadeIn(2000);
	});
}
 
 
/* Initialise external events */
function initialise_external_event(selector){
 
	//Initialise booking types
	$(selector).each(function(){
 
		//Make draggable
		$(this).draggable({
			revert: true,
			revertDuration: 0,
			zIndex: 999,
			cursorAt: {
				left: 10,
				top: 1
			}
		});
 
		//Create event object
		var event_object = {
			title: $.trim($(this).text())
		};
 
		//Store event in dom to be accessed later
		$(this).data('eventObject', event_object);
	});
}
 
 
/* Initialise color pickers */
function initialise_color_pickers(){
 
	//Initialise color pickers
	$('.color_picker').miniColors({
		'trigger': 'show',
		'opacity': 'none'
	});
}
 
 
/* Initialises calendar */
function initialise_calendar(){
 
	//Initialise calendar
	$('#calendar').fullCalendar({
		theme: true,
		firstDay: 1,
		header: {
			left: 'today prev,next',
			center: 'title',
			right: 'month,agendaWeek,agendaDay'
		},
		defaultView: 'agendaWeek',
		minTime: '6:00am',
		maxTime: '6:00pm',
		allDaySlot: false,
		columnFormat: {
			month: 'ddd',
			week: 'ddd dd/MM',
			day: 'dddd M/d'
		},
		eventSources: [
			{
				url: 'calendar_events.json',
				editable: false
			}
		],
		droppable: true,
		drop: function(date, all_day){
			external_event_dropped(date, all_day, this);
		},
		eventClick: function(cal_event, js_event, view){
			calendar_event_clicked(cal_event, js_event, view);
		},
		editable: true
	});	
 
	//Initialise external events
	initialise_external_event('.external-event');
}
 
 
/* Handle an external event that has been dropped on the calendar */
function external_event_dropped(date, all_day, external_event){
 
	//Create vars
	var event_object;
	var copied_event_object;
	var duration = 60;
	var cost;
 
	//Retrive dropped elemetns stored event object
	event_object = $(external_event).data('eventObject');
 
	//Copy so that multiple events don't reference same object
	copied_event_object = $.extend({}, event_object);
 
	//Assign reported start and end dates
	copied_event_object.start = date;
	copied_event_object.end = new Date(date.getTime() + duration * 60000);
	copied_event_object.allDay = all_day;
 
	//Assign colors etc
	copied_event_object.backgroundColor = $(external_event).data('background');
	copied_event_object.textColor = $(external_event).data('text');
	copied_event_object.borderColor = $(external_event).data('border');
 
	//Assign text, price, etc
	copied_event_object.id = get_uni_id();
	copied_event_object.title = $(external_event).data('title');
	copied_event_object.description = $(external_event).data('description');
	copied_event_object.price = $(external_event).data('price');
	copied_event_object.available = $(external_event).data('available');
 
	//Render event on calendar
	$('#calendar').fullCalendar('renderEvent', copied_event_object, true);
}
 
 
/* Initialise event clicks */
function calendar_event_clicked(cal_event, js_event, view){
 
	//Set generation values
	set_event_generation_values(cal_event.id, cal_event.backgroundColor, cal_event.borderColor, cal_event.textColor, cal_event.title, cal_event.description, cal_event.price, cal_event.available);
}
 
 
/* Set event generation values */
function set_event_generation_values(event_id, bg_color, border_color, text_color, title, description, price, available){
 
	//Set values
	$('#txt_background_color').miniColors('value', bg_color);
	$('#txt_border_color').miniColors('value', border_color);
	$('#txt_text_color').miniColors('value', text_color);
	$('#txt_title').val(title);
	$('#txt_description').val(description);
	$('#txt_price').val(price);
	$('#txt_available').val(available);
	$('#txt_current_event').val(event_id);
}
 
 
/* Generate unique id */
function get_uni_id(){
 
	//Generate unique id
	return new Date().getTime() + Math.floor(Math.random()) * 500;
}
 
 
/* Initialise update event button */
function initialise_update_event(){
	var test = $('#calendar').fullCalendar( 'clientEvents');
	//Bind event
	$('#btn_update_event').bind('click', function(){
 
		//Create vars
		var current_event_id = $('#txt_current_event').val();
 
		//Check if value found
		if(current_event_id){
 
			//Retrieve current event
			var current_event = $('#calendar').fullCalendar('clientEvents', current_event_id);
 
			//Check if found
			if(current_event && current_event.length == 1){
 
				//Retrieve current event from array
				current_event = current_event[0];
 
				//Set values
				current_event.backgroundColor = $('#txt_background_color').val();
				current_event.textColor = $('#txt_text_color').val();
				current_event.borderColor = $('#txt_border_color').val();
				current_event.title = $('#txt_title').val();
				current_event.description = $('#txt_description').val();
				current_event.price = $('#txt_price').val();
				current_event.available = $('#txt_available').val();
 
				//Update event
				$('#calendar').fullCalendar('updateEvent', current_event);
			}
		}
	});
}

Stylesheet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
body{
	font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
	font-size: 12px;
}
 
#content_wrapper{
	width: 820px;
	margin-left: auto;
	margin-right: auto;
}
 
#calendar{
	width: 650px;
	float: left;
}
 
.btn{
	font-size: 85%
	margin-top: 10px;
}
 
#event_generation_wrapper{
	float: left;
	width: 120px;
	background-color: #DDD;
	margin-left: 20px;
	margin-top: 40px;
	border: 1px solid #4297D7;
	background-color: #FCFDFD;	
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
	border-radius: 3px;
	padding: 10px 5px 10px 5px;
}
 
#event_generation_wrapper .left{
	float: left;
	width: 70px;
}
 
#event_generation_wrapper .right{
	float: left;
	max-width: 25px;
	margin-left: 10px;
}
 
#event_generation_wrapper input{
	width: 112px;
}
 
#event_generation_wrapper textarea{
	width: 110px;	
	height: 50px;
}
 
#event_generation_wrapper .miniColors-triggerWrap{	
	margin-bottom: 5px;
}
 
#event_generation_wrapper .text{
	padding-top: 1px;	
	margin-bottom: 5px;
	line-height: 10px;
}
 
 
#external_events{
	float: left;
	width: 130px;
	background-color: #DDD;
	margin-left: 20px;
	margin-top: 40px;
	border: 1px solid #4297D7;
	background-color: #FCFDFD;	
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
	border-radius: 3px;
}
 
#external_events .external-event{
	width: 100px;
	margin: 5px;
	font-family: Verdana, Arial, sans-serif;
	border: 1px solid #36C;
	padding: 3px;
	text-align: center;
	background-color: #36C;
	color: white;
	cursor: pointer;
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
	border-radius: 3px;
}
 
#calendar .ui-widget-header{
	font-weight: normal;
	padding: 3px 3px 3px 3px;
}
 
#calendar .fc-header-title{
	font-weight: normal;
}
 
#external_event_template{
	display: none;
}

Basic List of Events (JSON):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[
    "0",
    {
        "allDay": "",
        "title": "Test event",
        "id": "821",
        "start": "2012-11-23 14:00:00",
        "end": "2012-11-23 15:00:00"        
    },
    "1",
    {
        "allDay": "",
        "title": "Test event 2",
        "id": "822",        
        "start": "2012-11-23 9:00:00",
        "end": "2012-11-23 10:00:00"
    },
    "2",
    {
        "allDay": "",
        "title": "Test event 3",
        "id": "823",        
        "start": "2012-11-24 8:00:00",
        "end": "2012-11-24 6:00:00"
    },
    "3",
    {
        "allDay": "",
        "title": "Test event 4",
        "id": "824",        
        "start": "2012-11-27 6:00:00",
        "end": "2012-11-27 7:00:00"
    }
]

Let me know if you have any issues or something to add!

Cheers,
Chris