PullMonkey Blog


18 Aug

Open Flash Chart II - Point by Point plotting


Got another comment, this time asking about all kinds of things:

  1. Plotting Points
  2. X Axis and Y Axis min and max ranges
  3. Coloring the X and Y Axis
  4. Coloring the X and Y gridlines

Here is the graph we are after in this example:






More Open Flash Chart II examples.




And here is the code (the controller):

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

class TestItController < ApplicationController
  def index
    @graph = open_flash_chart_object(600,300,"/test_it/graph_code")
  end

  def graph_code
    chart = OpenFlashChart.new

    title = Title.new("Scatter points")
    chart.set_title(title)

    scatter = Scatter.new('#FFD800', 10)  # color, dot size

    scatter.values = [
      ScatterValue.new(50,30),
      ScatterValue.new(305,400),
      ScatterValue.new(61,500,15),  # x, y, dot size
      ScatterValue.new(600,550),
      ScatterValue.new(459,300),
      ScatterValue.new(180,789)
    ]

    chart.add_element(scatter)

    x = XAxis.new
    x.set_range(0, 650, 100)  #min, max, steps
    # alternatively, you can use x.set_range(0,65000) and x.set_step(10000)
    x.colour = '#00FF00'
    # have to set the x axis labels because of scatter bug here - http://sourceforge.net/forum/message.php?msg_id=4812326
    x.set_grid_colour('#00F0FF')
    chart.set_x_axis(x)

    y = YAxis.new
    y.set_range(0,800,200)
    y.colour = '#FF0000'
    y.set_grid_colour('#FF00FF')
    chart.set_y_axis(y)

    render :text => chart.to_s
  end
end



And in your view (index.html.erb):

1
2
3
4

<script type="text/javascript" src="/javascripts/swfobject.js"></script>
<%= @graph %>





Note - The X Axis is not rendering properly, this is being discussed in the OFC Forums.


Good Luck!





13 Aug

Modularized Models and Associations in ActiveRecord


Have you ever encountered this error?

wrong constant name X::YAssociationExtension

I found this error when dealing with acts_as_versioned for one my models. I would have found it eventually with any association I would have setup in this case, so it is not acts_as_versioned's fault.

I have an application with various parts so I create some of my models like this:

1
2
./script/generate model module_name::model_name



So the fix is pretty simple, instead of winding up with ModuleName::ModelAssociationExtension or whatever, you want your extention module name to be ModelAssociationExtension.

Here is the method where the code breaks for me:

1
2
3
4
5
6
7
8
9
10
11
12
13
def create_extension_modules(association_id, block_extension, extensions)
  if block_extension
    extension_module_name = "#{self.to_s}#{association_id.to_s.camelize}AssociationExtension"

    silence_warnings do
      Object.const_set(extension_module_name, Module.new(&block_extension))
    end
    Array(extensions).push(extension_module_name.constantize)
  else
    Array(extensions)
  end
end

Here is the fix, on line 3, you will note the demodulize addition:

1
2
3
4
5
6
7
8
9
10
11
12
13
def create_extension_modules(association_id, block_extension, extensions)
  if block_extension
    extension_module_name = "#{self.to_s.demodulize}#{association_id.to_s.camelize}AssociationExtension"

    silence_warnings do
      Object.const_set(extension_module_name, Module.new(&block_extension))
    end
    Array(extensions).push(extension_module_name.constantize)
  else
    Array(extensions)
  end
end

I threw the corrected code into a library - RAILS_ROOT/lib/fix_active_record_create_extension_for_modules.rb. Make sure to open up the necessary modules to overwrite the method itself, I.e., like this:

1
2
3
4
5
6
7
8
module ActiveRecord
  module Associations
    module ClassMethods
      # the code above fits right here
    end
  end
end

Short and sweet, let me know if you have a better method for handling this.





05 Aug

Open Flash Chart II - X Axis Label rotations


Got another comment asking about diagonal x axis labels. This example is based on teethgrinder's line graph example and the new portions are based on teethgrinder's x-axis rotation example.

Here is the graph we are after in this example:







More Open Flash Chart II examples.




And here is the code (the controller):

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

class TestItController < ApplicationController
  def index
    @graph = open_flash_chart_object(600,300,"/test_it/graph_code")
  end

  def graph_code
    # based on this example - http://teethgrinder.co.uk/open-flash-chart-2/data-lines-2.php
    # and parts from this example - http://teethgrinder.co.uk/open-flash-chart-2/x-axis-labels-3.php
    title = Title.new("Multiple Lines")

    data1 = []
    data2 = []
    data3 = []

    10.times do |x|
    data1 << rand(5) + 1
    data2 << rand(6) + 7
    data3 << rand(5) + 14
    end

    line_dot = LineDot.new
    line_dot.width = 4
    line_dot.colour = '#DFC329'
    line_dot.dot_size = 5
    line_dot.values = data1

    line_hollow = LineHollow.new
    line_hollow.width = 1
    line_hollow.colour = '#6363AC'
    line_hollow.dot_size = 5
    line_hollow.values = data2

    line = Line.new
    line.width = 1
    line.colour = '#5E4725'
    line.dot_size = 5
    line.values = data3

    # Added these lines since the previous tutorial
    tmp = []
    x_labels = XAxisLabels.new
    x_labels.set_vertical()

    %w(one two three four five six seven eight nine ten).each do |text|
      tmp << XAxisLabel.new(text, '#0000ff', 20, 'diagonal')
    end

    x_labels.labels = tmp

    x = XAxis.new
    x.set_labels(x_labels)
    # new up to here ...

    y = YAxis.new
    y.set_range(0,20,5)

    x_legend = XLegend.new("MY X Legend")
    x_legend.set_style('{font-size: 20px; color: #778877}')

    y_legend = YLegend.new("MY Y Legend")
    y_legend.set_style('{font-size: 20px; color: #770077}')

    chart =OpenFlashChart.new
    chart.set_title(title)
    chart.set_x_legend(x_legend)
    chart.set_y_legend(y_legend)
    chart.x_axis = x # Added this line since the previous tutorial
    chart.y_axis = y

    chart.add_element(line_dot)
    chart.add_element(line_hollow)
    chart.add_element(line)

    render :text => chart.to_s
  end
end



And in your view (index.html.erb):

1
2
3
4

<script type="text/javascript" src="/javascripts/swfobject.js"></script>
<%= @graph %>





Good Luck!





05 Aug

Dynamic Select Boxes (many to many) - Ruby on Rails


I just got comment asking how one would go about doing a many to many relation in this dynamic select box example. For example, what if an artist belongs to multiple genres. Here we go:

The original tutorial.

Create your models and build your migrations:

1
2
3
4
5

ruby script/generate model genre name:string
ruby script/generate model artist name:string   # no genre_id here, moved to association table
ruby script/generate model song title:string artist_id:integer
ruby script/generate model artist_association artist_id:integer genre_id:integer

Populate your genres, artists and songs through a migration:

1
2

ruby script/generate migration create_hierarchy

Contents of migration:

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

class CreateHierarchy < ActiveRecord::Migration
  def self.up
    # same genres as before
    g1 = Genre.create(:name => "Genre 1")
    g2 = Genre.create(:name => "Genre 2")
    g3 = Genre.create(:name => "Genre 3")

    # same artists as before, but without a genre_id
    a1 = Artist.create(:name => "Artist 1")
    a2 = Artist.create(:name => "Artist 2")
    a3 = Artist.create(:name => "Artist 3")
    a4 = Artist.create(:name => "Artist 4")
    a5 = Artist.create(:name => "Artist 5")
    a6 = Artist.create(:name => "Artist 6")

    # now set which artists belong to which genres
    # Artist 1 belongs to all three genres
    ArtistAssociation.create(:genre_id => g1.id, :artist_id => a1.id)
    ArtistAssociation.create(:genre_id => g2.id, :artist_id => a1.id)
    ArtistAssociation.create(:genre_id => g3.id, :artist_id => a1.id)
   # the rest of the artists only belong to one association
    ArtistAssociation.create(:genre_id => g1.id, :artist_id => a2.id)
    ArtistAssociation.create(:genre_id => g2.id, :artist_id => a3.id)
    ArtistAssociation.create(:genre_id => g2.id, :artist_id => a4.id)
    ArtistAssociation.create(:genre_id => g3.id, :artist_id => a5.id)
    ArtistAssociation.create(:genre_id => g3.id, :artist_id => a6.id)

    Song.create(:title => "Song 1",  :artist_id => a1.id)
    Song.create(:title => "Song 2",  :artist_id => a1.id)
    Song.create(:title => "Song 3",  :artist_id => a2.id)
    Song.create(:title => "Song 4",  :artist_id => a2.id)
    Song.create(:title => "Song 5",  :artist_id => a3.id)
    Song.create(:title => "Song 6",  :artist_id => a3.id)
    Song.create(:title => "Song 7",  :artist_id => a4.id)
    Song.create(:title => "Song 8",  :artist_id => a4.id)
    Song.create(:title => "Song 9",  :artist_id => a5.id)
    Song.create(:title => "Song 10", :artist_id => a5.id)
    Song.create(:title => "Song 11", :artist_id => a6.id)
    Song.create(:title => "Song 12", :artist_id => a6.id)
  end

  def self.down
# you can fill this in if you want.
  end
end

So now we need to populate the database:

1
2

rake db:migrate

Now we need to modify our models to set up the associations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

class Genre < ActiveRecord::Base
  has_many :artist_associations
  has_many :artists, :through => :artist_associations
  # CAN"T NEST HMTs .....  has_many :songs, :through => :artists
  # do it by hand ... argh
  def songs
    artists.map{|a| a.songs}.flatten
  end
end

class Artist < ActiveRecord::Base
  has_many :artist_associations
  has_many :genres, :through => :artist_associations
  has_many :songs
end

class ArtistAssociation < ActiveRecord::Base
  belongs_to :artist
  belongs_to :genre
end

That should be it for the many to many relationship.



Everything else is the same as in the last tutorial.

# the controller

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

class TestItController < ApplicationController
  def index
    @genres  = Genre.find(:all)
    @artists = Artist.find(:all)
    @songs   = Song.find(:all)
  end

  def update_artists
    # updates artists and songs based on genre selected
    genre = Genre.find(params[:genre_id])
    artists = genre.artists
    songs   = genre.songs

    render :update do |page|
      page.replace_html 'artists', :partial => 'artists', :object => artists
      page.replace_html 'songs',   :partial => 'songs',   :object => songs
    end
  end

  def update_songs
    # updates songs based on artist selected
    artist = Artist.find(params[:artist_id])
    songs  = artist.songs

    render :update do |page|
      page.replace_html 'songs', :partial => 'songs', :object => songs
    end
  end
end

Now as far as views go we have one view (index.html.erb) and two partials (_songs and _artists). Let's take a look at those:

# the _songs partial (_songs.html.erb):

1
2
3

<%= collection_select(nil, :song_id, songs, :id, :title,
                     {:prompt   => "Select a Song"}) %>

# the _artists partial (_artists.html.erb):

1
2
3
4
5
6

<%= collection_select(nil, :artist_id, artists, :id, :name,
                     {:prompt   => "Select an Artist"},
                     {:onchange => "#{remote_function(:url  => {:action => "update_songs"},
                                                      :with => "'artist_id='+value")}"}) %>
<br/>

# and last, but not least, the index view (index.html.erb):

1
2
3
4
5
6
7
8
9

<%= javascript_include_tag :defaults %>
<%= collection_select(nil, :genre_id,  @genres,  :id, :name,
                      {:prompt   => "Select a Genre"},
                      {:onchange => "#{remote_function(:url  => {:action => "update_artists"},
                                                       :with => "'genre_id='+value")}"}) %>
<br/>
<div id="artists"><%= render :partial => 'artists', :object => @artists %></div>
<div id="songs"><%= render :partial => 'songs',   :object => @songs %></div>




04 Aug

Open Flash Chart II - Multiple graphs on the same page


Got a comment asking about multiple graphs on the same page and if you need another controller or how that would all work. You only need one controller if you are going to have different graphs in the same view.

No graph for this example, just the code.

Open Flash Chart II examples.




Here is the code (the controller):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class TestItController < ApplicationController
  def index
    @graph1 = open_flash_chart_object(600,300,"/test_it/graph_one")
    @graph2 = open_flash_chart_object(600,300,"/test_it/graph_two")
    @graph3 = open_flash_chart_object(600,300,"/test_it/graph_three")
    # and so on ...
  end

  def graph_one
    ...  # put some OFC2 code here like in my examples
  end

  def graph_two
    ...  # put some OFC2 code here
  end

  def graph_three
    ...  # put some OFC2 code here
  end
end



And in your view (index.html.erb):

1
2
3
4
5
6

<script type="text/javascript" src="/javascripts/swfobject.js"></script>
<%= @graph1 %><br/>
<%= @graph2 %><br/>
<%= @graph3 %><br/>





Good Luck!





01 Aug

Open Flash Chart II - Line Graph


Got another comment asking about creating a line graph. This example is based on teethgrinder's line graph example.

Here is the graph we are after in this example:







More Open Flash Chart II examples.




And here is the code (the controller):

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

class TestItController < ApplicationController
  def index
    @graph = open_flash_chart_object(600,300,"/test_it/graph_code")
  end

  def graph_code
    # based on this example - http://teethgrinder.co.uk/open-flash-chart-2/data-lines-2.php
    title = Title.new("Multiple Lines")

    data1 = []
    data2 = []
    data3 = []

    10.times do |x|
      data1 << rand(5) + 1
      data2 << rand(6) + 7
      data3 << rand(5) + 14
    end

    line_dot = LineDot.new
    line_dot.text = "Line Dot"
    line_dot.width = 4
    line_dot.colour = '#DFC329'
    line_dot.dot_size = 5
    line_dot.values = data1

    line_hollow = LineHollow.new
    line_hollow.text = "Line Hollow"
    line_hollow.width = 1
    line_hollow.colour = '#6363AC'
    line_hollow.dot_size = 5
    line_hollow.values = data2

    line = Line.new
    line.text = "Line"
    line.width = 1
    line.colour = '#5E4725'
    line.dot_size = 5
    line.values = data3

    y = YAxis.new
    y.set_range(0,20,5)

    x_legend = XLegend.new("MY X Legend")
    x_legend.set_style('{font-size: 20px; color: #778877}')

    y_legend = YLegend.new("MY Y Legend")
    y_legend.set_style('{font-size: 20px; color: #770077}')

    chart =OpenFlashChart.new
    chart.set_title(title)
    chart.set_x_legend(x_legend)
    chart.set_y_legend(y_legend)
    chart.y_axis = y

    chart.add_element(line_dot)
    chart.add_element(line_hollow)
    chart.add_element(line)

    render :text => chart.to_s
  end
end



And in your view (index.html.erb):

1
2
3
4

<script type="text/javascript" src="/javascripts/swfobject.js"></script>
<%= @graph %>





Good Luck!