PullMonkey Blog


24 Jan

VIN API – fair pricing model


It can't be a shock to any of you that after many months (wait - more like a full year) of running VIN API completely free and out-of-pocket, it is time to monetize. As I am sure most of you know, the back-end data is quite pricey. For example, a monthly subscription for a "lite" data set (your typical year, make, model, engine type, etc) at one of the better known distributors would run you about $260.00 a month for ~800 VIN decodings. This is some very expensive data at 32.5 cents a VIN. Some of you that use our API are running 50,000+ VINs through in a weekend, that is $16,000! Wow!

Ok, so what did we decide to do? Well first, and most importantly, we decided to keep the service up and running; for a while there it was looking pretty bleak and if you take a second to look at the numbers (financially) you can understand why. Anyway, to be as fair as possible, we did not feel this could be monetized as a subscription-based product, some months you need 500,000 VINs and some only a 1,000. Looking at the data for year, we had many questions - what plan would you pick? should the plan rollover it's unused decodings?

We discovered a lot of complexity in the subscription model, so we decided to setup the pay as you go plan, where you buy your decodings at various bulk levels. For instance, you could buy in groups of 1,000 or 10,000 or 100,000 such that you would realize savings on a price-per-VIN basis the larger the group you purchase. There is no use policy either, so you can sit on the VINs for as long as you want, or you can even buy decodings the day (even the minute) before you need them. No monthly credit card bill, no rollovers, just simple "buy what you need," and if you buy in bulk, you save.

Another consideration in doing this was having to maintain soft limits. For example, if the user purchases the 5,000 VINs per month plan, and the their site does well this month and they need 6000 VINs, should they have chosen the 10,000 VIN plan? We didn't think so, we figured no hard limit, just soft limits, and after the soft limit is hit, we would charge the additional VINs at the current rate per VIN to the user's next month's bill. We tried to explain this to a few current users that are helping us come up with reasonable rates and they were not all that thrilled about it. So we had a problem, we did not want users to pay for what they did not need, but at the same time we did not want to cut users off in the middle of a month when they hit their limit. That entire idea had to be scrapped and along with it went the idea behind the subscription plan itself. We like it though, it's simple now - buy your decodings and use your decodings, we will email you if you are getting low and may want to add decodings to your account.

Subscription models are great for static resources and static services, but not a single one of our customers decodes the same number of VINs each month, so it just won't work. So onward and upward, pay as you go and regardless of how the other API providers offer their service, we are excited to be a little different and lot cheaper as you will soon find out.

Enjoy!


06 Jan

POST OFC Graph as Image


I was asked recently (well sort of) to give an example of saving an image to the server. If you look at teethgrinder's example for this, you will see that he has made available an external interface to do just that - POST your graph as png raw data to your server for storage. This has many benefits such as saving the image for use in a PDF report or for printing, since we know at times it is a bit troublesome to print the embedded flash object.
I think the main problem people are having with this is the receiving of the image data post - see the upload_image method below. Also, teethgrinder's example never really says where to make the post_image() call. So I touch on both in the code below.
Here is an example of the png that is saved when I did this for the chart in the previous example:
OFC Saved Image

Well, let's just get right in to the code.
The controller contains the same code as my last post with only a few minor changes to the index method and the addition of the upload_image method.
In the controller, I have this:

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
    # note the user of open_flash_chart_object_from_hash instead of just open_flash_chart_object
    # this allows you to pass in the id of the div you want the the chart to be in
    # this is useful for when we need to findSWF by this id
    @graph = open_flash_chart_object_from_hash("/test_it/chart", :div_name => "my_chart")
  end

  # added to recieve the post data for the OFC png image of the OFC graph
  def upload_image
    name = "tmp_image.png" || params[:name]
    # the save_image method that is provided by the OFC swf file sends raw post data, so get to it like this
    data = request.raw_post
    File.open("#{RAILS_ROOT}/tmp/#{name}", "wb") { |f| f.write(data) } if data
    render :nothing => true
  end

  def chart
    # same code from here - http://pullmonkey.com/2010/01/05/open-flash-chart-ii-x-axis-date-and-time/ 
    ...
  end
end

So just note the use of open_flash_chart_object_from_hash() in the index method, this way we can pass in the id of the div.
In the view, I have this:

1
2
3
4
5
<%= javascript_include_tag 'swfobject.js' %>
<%= @graph %>
<%= save_as_image("http://localhost:3000/test_it/upload_image?name=tmp.png", :id => "my_chart") %>
<br/>
<%= button_to_function "Save Image", "post_image()" %>

Really the only difference from what we would normally have in our view is that I am using the save image setup method that was added to the open flash chart ruby on rails plugin in the last couple hours (as of this post). The save_image method takes some arguments, mainly the url to post the image data to and the id of the chart we setup in the controller.


30 Dec

Using Tumblr as a CMS


Thought you all might like this - http://blog.skizmo.com/post/308406755/use-tumblr-as-your-cms

It is something we sort of dreamed up and it works great as a partial CMS - very much like SimpleCMS where you can specify what exactly on the page needs to be managed by a CMS. This allows you to mix your CMS static content with your dynamic content.


Comments Off on Using Tumblr as a CMS Filed under: development, rails, ruby, SimpleCMS, tutorials Tags: , , , , , , , ,
21 Sep

Open Flash Chart II – Javascript (Part 1)


This article (and the work behind it -- meaning get the latest from github) is generously sponsored by Harry Seldon who wants to be able to pass data around using javascript. There are quite a few benefits to this, learn more from teethgrinder's tutorial on the same topic.

This example opens up a lot of possibilities and I thank Harry for pointing me to it. So more to come on OFC and Javascript. For a taste of what is to come, check these out:

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

class TestItController < ApplicationController
  def index
    title = Title.new("MY TITLE")
    bar = BarGlass.new
    bar.set_values([1,2,3,4,5,6,7,8,9])
    @chart = OpenFlashChart.new
    @chart.set_title(title)
    @chart.add_element(bar)
  end
end

Notice that I do not render the chart object, however I turn it into an instance variable for use in our javascript rendering of our chart.

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

1
2
3
4
5
6
7
8
9

<html>
  <head>
    <%= javascript_include_tag :defaults, "swfobject" %>
  </head>
  <body>
    <%= @chart.js_open_flash_chart_object("my_chart_js_1", 550,300) %>
  </body>
</html>

I do a lot behind the scenes but if you look, you will see a few new things here.

  1. The data comes from the @chart.js_open_flash_chart_object(...) call which sets up a few javascript callback methods to send the data to the SWF object. It takes 3 required arguments div_name (the id of the div that houses the chart), width and height and one optional argument base_url which defaults to "/".
  2. If you look at the HTML source, it is quite a bit different, we simply embed the SWF object. We do not need to point to the data method since there actually isn't one.
  3. One difference between this Rails example and php example (from teethgrinder) is that prototype which comes standard with rails, has a JSON implementation for javascript, so we do not need the json2.js file, but make sure to include prototype.js in your apps.

For more information on the javascript callbacks that I setup here, you will want to view the page source and read about it further over at teethgrinder's tutorial.

Good Luck! and Harry, I hope this helps, otherwise please drop me a line.


24 Jul

simpleCMS on github


Moved simpleCMS over to github.

There were quite a few changes that were required to get it working with rails 2.1, so they are in the repository now.
So to install, you pretty much follow the same instructions from before.

The only thing I have not completely resolved is the use of the simple_cms_item partial that sits in the plugin's app/views/shared directory. I tried forever to use append_view_path to share the partial over. That worked, as in it found the partial, but the application layout went away, so instead, you can just copy the partial to your RAILS_ROOT/app/views/shared directory for now.

Additionally, you can install it through github: script/plugin install git://github.com/pullmonkey/simple_cms.git
And of course, a lot of you have asked how to just plain download it, well you can do that here, find the download button and click 🙂

Let me know how it goes.


28 Feb

Changes/Fixes to the Simple CMS Plugin


You can also view the tutorial. Or you can check out the demo.

We are continuously adding to and changing the Simple CMS Plugin so here is a little rundown of what has been changed so far:

Major Changes:

  • I have updated to rails 2.0.2 so if you are using an older version you will need to change a line in each of the controllers.
  • 1
    2
    3
    4
    5
    6
    
    
    # Where it has:
      self.view_paths << File.join(File.dirname(__FILE__), '..', 'views')
    # Must be Changed to:
      self.template_root = File.join(File.dirname(__FILE__), '..', 'views')
    
    
  • The acts_as_versioned plugin is now required for the revisions. I added this quite some time ago. I did change the rake task to install this plugin as well as the rest.
  • You now have to pass a :prefix if you have one. This is to ensure that all content such as images, media, smiles, etc. work correctly. If your base path is a standard path then you do not need to worry about this.
    For example: I was running 3 rails applications under one domain so it looked like this:

    my.domain.com /blog
    /forum
    /demo

    So I would call my Simple CMS content like this for blog:

  • 1
    2
    3
    
    
    <%= render :simpleCMS => "MyBlog", :admin => true, :prefix => "/blog" %>
    
    
  • Now you can make content viewable and editable from multiple pages by passing :reusable => true. This defaults to false. Your label must be the same in each place you use it. This is very useful for layouts, such as header and footer.
    *NOTE* If you already have content here and you change this variable you will lose all that content as it gives the content a new id and changes the params and how the content is called. For example if I have :reusable set to false and I have content there and I set :reusable to true then it creates content with a different id so I will not be able to use the old content until I change it back to false.

Minor Changes:

  • When you click on a revision you are taken back up to the top of the page instead of having to scroll all the way down a list of 100 revisions, clicking on one, and then manually scrolling all the way back up to the top to see if you even picked the right revision
  • I have added code highlighting and this requires the coderay plugin, which has been added to the rake simple_cms:install_dependencies, and a coderay stylesheet so you need to add that to your application layout. I have included it in the rake simple_cms:install
  • 1
    2
    3
    
    
    <%= stylesheet_link_tag "coderay" %>
    
    
  • I have also changed many things in the javascripts inside the tiny_mce editor so it would be safest just to use the rake tasks that are built in:
  • 1
    2
    3
    4
    
    
    rake simple_cms:uninstall
    rake simple_cms:install
    
    

I have updated the tutorial with all the changes as well.

There is also a demo that you can use to play around with it and ask questions.


02 Feb

Simple CMS Plugin for Rails – with revisions


Well, we have been working on SimpleCMS a little and just for kicks we added revisions.
Check out the Simple CMS Plugin Demo
On this page you will see the "Show all revisions" link at the bottom of the "edit" page.
So feel free to add or delete or change revisions at will.
Also, if you are new to SimpleCMS, check out the sort of rough getting stared page.
Well, enjoy and please leave comments/feedback/requests here in the comments or preferably in the list of requests we have started on the demo page 🙂


30 Dec

Simple CMS Plugin for Rails Demo


I have placed a nice little demo of this plugin under Pullmonkey Projects - http://pullmonkey.com/projects/simple_cms.

Please let us know what works, what does not work.

Thank you.


Comments Off on Simple CMS Plugin for Rails Demo Filed under: development, Home, projects, rails, ruby, SimpleCMS Tags: , , , , , ,
23 Dec

Simple CMS Plugin for Ruby on Rails Tutorial


The SimpleCMS Plugin

Created by Slaive and PullMonkey (December 2007)

Check out the demo - http://pullmonkey.com/projects/simple_cms

This is still a work in progress so feel free to notify me of any bugs, problems, or suggestions of how to make it better.

This plugin is built for rails 2.0.2. So if you are using an older version of rails then you will need to edit each of the controllers

1
2
3
4
5
6
7

From:
  self.view_paths << File.join(File.dirname(__FILE__), '..', 'views')

To:
  self.template_root = File.join(File.dirname(__FILE__), '..', 'views')

Imaging Processor

For this plugin to be fully functional you will need to install one of the following Image Processing gems:

  • ImageScience - A light inline-Ruby library that only resizes images.
  • RMagick - The grand-daddy, both in terms of advanced image processing features and memory usage.
  • minimagick - It's much easier on memory than RMagick because it runs the ImageMagick command in a shell.

Any one of these gems will work.

Install SimpleCMS and Dependencies

1
2
3

ruby script/plugin install http://svn.pullmonkey.com/plugins/trunk/simple_cms/

The simple_cms plugin requires the attachment_fu, responds_to_parent, acts_as_versioned, and coderay plugins as well. To make this easier I there is a built-in rake process

1
2
3

rake simple_cms:install_dependencies

However if this doesn't work then you can do it the normal way:

1
2
3
4
5
6

ruby script/plugin install http://svn.pullmonkey.com/plugins/trunk/attachment_fu/
ruby script/plugin install http://svn.pullmonkey.com/plugins/trunk/responds_to_parent/
ruby script/plugin install http://svn.pullmonkey.com/plugins/trunk/acts_as_versioned/
ruby script/plugin install http://svn.pullmonkey.com/plugins/trunk/coderay/

The Javascript/css Files

This plugin requires a great deal of javascript and css files that will need to be copied to the corresponding folder in your public/ directory. These files are located in the simple_cms/assets/ directory.

These files should have been copied over when you install the plugin. However, I built in a couple rake task commands to help you out installing and uninstalling these files if you need to.

1
2
3
4

rake simple_cms:install
rake simple_cms:uninstall

Creating the Tables

You will need to create 3 tables in your database.

  • simple_cms_items
  • simple_cms_images
  • simple_cms_media

To have the migration tables generated for you use this command:

1
2
3

ruby script/generate simple_cms_migrations

The tables should look like this:

create_simple_cms_items.rb

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

class CreateSimpleCmsItems < ActiveRecord::Migration
  def self.up
    create_table :simple_cms_items do |t|
      t.column :params,     :string
      t.column :data,       :text
      t.column :position,   :integer
      t.column :created_at, :datetime
      t.column :updated_at, :datetime
      t.column :created_by, :string
      t.column :updated_by, :string
    end
    SimpleCmsItem.create_versioned_table
  end

  def self.down
    SimpleCmsItem.drop_versioned_table
    drop_table :simple_cms_items
  end
end

create_simple_cms_images.rb

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

class CreateSimpleCmsImages < ActiveRecord::Migration
  def self.up
    create_table :simple_cms_images do |t|
      t.column :parent_id,    :integer
      t.column :content_type, :string
      t.column :filename,     :string
      t.column :thumbnail,    :string
      t.column :size,         :integer
      t.column :width,        :integer
      t.column :height,       :integer
    end
  end

  def self.down
    drop_table :simple_cms_images
  end
end

create_simple_cms_medias.rb

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

class CreateSimpleCmsMedias < ActiveRecord::Migration
  def self.up
    create_table :simple_cms_medias do |t|
      t.column :parent_id,    :integer
      t.column :content_type, :string
      t.column :filename,     :string
      t.column :thumbnail,    :string
      t.column :size,         :integer
      t.column :width,        :integer
      t.column :height,       :integer
    end
  end

  def self.down
    drop_table :simple_cms_medias
  end
end

There is one more migration file you should have. It's called change_items_data_colmn and it looks like this:

1
2
3
4
5
6
7
8
9
10
11

class ChangeItemsDataColumn < ActiveRecord::Migration
  def self.up
    change_column :simple_cms_items, :data, :text, :limit => 10000000
  end

  def self.down
    change_column :simple_cms_items, :data, :text
  end
end

This is a change to the simple_cms_items table data column. This allows you to store up to 10 megabytes of text instead of the 65 kilobytes it defaulted to.

Remember to rake your tables into your databases once you have generated them.

1
2
3

rake db:migrate

Javascript and css Include Tags

You will need to make sure you have javascript include tags for your defaults and simple_cms and stylesheet link tags for the simple_cms and coderay stylesheets. Your app/views/layouts/application.rhtml should look something like this:

1
2
3
4
5
6
7
8
9
10
11

<html>
  <head>
    <%= javascript_include_tag :defaults, "simple_cms" %>
    <%= stylesheet_link_tag "simple_cms", "coderay" %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

In Your View

Having the simple_cms plugin show up is really pretty simple. Anywhere you want to have the simple_cms to show up you put one line as simple as this:

1
2
3

<%= render :simpleCMS => "YourLabel", :admin => true %>

Here is another example with more options:

1
2
3
4
5
6

<%= render :simpleCMS => "label", :admin => true,
                                  :user => "UserName",
                                  :prefix => "/whatever/your/prefix/is",
                                  :reusable => true %>

  • :simpleCMS takes a label. Changing this label means losing all your current content and creating a new one. However, you can always change it back.
  • :admin takes true or false. The default is false. If you pass true then when you run your mouse over the content a blue highlight box will appear and you will be able to click on the box and edit the content. If you pass false then you will only be able to see the content but not edit any of it.
  • :user is optional and it takes any string you pass it.
  • :prefix is optional. This is where you pass your prefix if you have one.
  • :reusable takes true or false. Default is false. Set this true if you want to use this same content on multiple pages. The label must be the same on all pages you are using this content. *NOTE* you will lose all current data for this content if you change this as it gives the content a different id and changes the params of how it is called.