chameleon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +108 -0
  3. data/app/controllers/widgets_controller.rb +20 -0
  4. data/app/models/widget.rb +47 -0
  5. data/app/views/widgets/number_and_secondary.xml.erb +11 -0
  6. data/config/routes.rb +3 -0
  7. data/lib/chameleon.rb +14 -0
  8. data/lib/generators/chameleon/widget_generator.rb +22 -0
  9. data/test/chameleon_test.rb +7 -0
  10. data/test/dummy/Rakefile +7 -0
  11. data/test/dummy/app/controllers/application_controller.rb +3 -0
  12. data/test/dummy/app/helpers/application_helper.rb +2 -0
  13. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  14. data/test/dummy/app/widgets/test_widget.rb +7 -0
  15. data/test/dummy/app/widgets/test_widget2.rb +8 -0
  16. data/test/dummy/config.ru +4 -0
  17. data/test/dummy/config/application.rb +45 -0
  18. data/test/dummy/config/boot.rb +10 -0
  19. data/test/dummy/config/database.yml +22 -0
  20. data/test/dummy/config/environment.rb +5 -0
  21. data/test/dummy/config/environments/development.rb +26 -0
  22. data/test/dummy/config/environments/production.rb +49 -0
  23. data/test/dummy/config/environments/test.rb +35 -0
  24. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  25. data/test/dummy/config/initializers/inflections.rb +10 -0
  26. data/test/dummy/config/initializers/mime_types.rb +5 -0
  27. data/test/dummy/config/initializers/secret_token.rb +7 -0
  28. data/test/dummy/config/initializers/session_store.rb +8 -0
  29. data/test/dummy/config/locales/en.yml +5 -0
  30. data/test/dummy/config/routes.rb +58 -0
  31. data/test/dummy/db/test.sqlite3 +0 -0
  32. data/test/dummy/log/development.log +2 -0
  33. data/test/dummy/log/production.log +0 -0
  34. data/test/dummy/log/server.log +0 -0
  35. data/test/dummy/log/test.log +334 -0
  36. data/test/dummy/public/404.html +26 -0
  37. data/test/dummy/public/422.html +26 -0
  38. data/test/dummy/public/500.html +26 -0
  39. data/test/dummy/public/favicon.ico +0 -0
  40. data/test/dummy/public/javascripts/application.js +2 -0
  41. data/test/dummy/public/javascripts/controls.js +965 -0
  42. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  43. data/test/dummy/public/javascripts/effects.js +1123 -0
  44. data/test/dummy/public/javascripts/prototype.js +6001 -0
  45. data/test/dummy/public/javascripts/rails.js +175 -0
  46. data/test/dummy/script/rails +6 -0
  47. data/test/functional/widgets_test.rb +31 -0
  48. data/test/integration/navigation_test.rb +7 -0
  49. data/test/support/integration_case.rb +5 -0
  50. data/test/test_helper.rb +22 -0
  51. data/test/unit/widget_test.rb +13 -0
  52. metadata +131 -0
@@ -0,0 +1,20 @@
1
+ Copyright 2010 Elliott Draper <el@ejdraper.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,108 @@
1
+ = Chameleon
2
+
3
+ * http://github.com/ejdraper/chameleon
4
+
5
+ == DESCRIPTION:
6
+
7
+ This is a Rails engine designed to make it super easy to expose data from your application as widgets for use with Geckoboard (http://geckoboard.com).
8
+
9
+ This only works for Rails 3 apps.
10
+
11
+ == FEATURES:
12
+
13
+ This provides a generator to build widgets using a basic DSL syntax to describe the data to be exposed, and the settings for it to be exposed under, and also includes the controller/routing necessary to provide an endpoint for Geckoboard to retrieve the data to display on your dashboard.
14
+
15
+ Currently it supports just the "number and optional secondary stat" widget type, with more widget types to follow shortly.
16
+
17
+ == SYNOPSIS:
18
+
19
+ Simply add to your Gemfile as follows:
20
+
21
+ gem 'chameleon'
22
+
23
+ Then run "bundle install" and once installed, you can run:
24
+
25
+ rails g chameleon:widget NAME TYPE
26
+
27
+ to generate a widget within "app/widgets". For example, if you want to generate a widget that exposes the amount of users you have in your app currently:
28
+
29
+ rails g chameleon:widget users number_and_secondary
30
+
31
+ This will generate something similar to the following in "app/widgets/users_widget.rb":
32
+
33
+ widget :users do
34
+ key "3638c90ec12d5a59061ad7b78afcbd050e50b621"
35
+ type "number_and_secondary"
36
+ data do
37
+ end
38
+ end
39
+
40
+ To implement your widget, you simply need to alter it to return the required data - for a "number_and_secondary" type of widget, it requires an array containing two values, the first being the current value, and the last being the value to compare it to (such as user count as of yesterday, for example):
41
+
42
+ widget :users do
43
+ key "3618c90ec02d5a57061ad7b78afcbb050e50b608"
44
+ type "number_and_secondary"
45
+ data do
46
+ [User.count, User.count(:conditions => "created_at < '#{1.day.ago.to_s(:db)}'")]
47
+ end
48
+ end
49
+
50
+ You can then point Geckoboard at your widget, http://myapp/widgets/users?key=3618c90ec02d5a57061ad7b78afcbb050e50b608, and Geckoboard will then show the current user count, as well as the percentage difference compared to a day ago. You can optionally just return a single value too, if you don't want the secondary stat.
51
+
52
+ By default the widgets are secured by the key which is autogenerated within the widget - this has to be passed in via the querystring for the widget to function, as seen above. You can change this value to anything you consider secure, and if you require a parameter other than :key to be used for this security check, you can define the parameter as such:
53
+
54
+ widget :users do
55
+ key "my_updated_key"
56
+ key_parameter :token
57
+ type "number_and_secondary"
58
+ data do
59
+ [User.count, User.count(:conditions => "created_at < '#{1.day.ago.to_s(:db)}'")]
60
+ end
61
+ end
62
+
63
+ This will now expect the key to be passed in as the :token parameter, for example http://myapp/widgets/users?token=my_updated_key. For data that is acceptable to be publically exposed, you can exclude the key and set the widget as public as follows:
64
+
65
+ widget :users do
66
+ public true
67
+ type "number_and_secondary"
68
+ data do
69
+ [User.count, User.count(:conditions => "created_at < '#{1.day.ago.to_s(:db)}'")]
70
+ end
71
+ end
72
+
73
+ This will now be accessible simply at http://myapp/widgets/users.
74
+
75
+ Full documentation on the different widget types and data will follow once the other widget types are implemented, but this basic syntax should provide an easy way to expose data from your Rails 3 application to Geckoboard quickly and easily.
76
+
77
+ == REQUIREMENTS:
78
+
79
+ A Rails 3 application.
80
+
81
+ == INSTALL:
82
+
83
+ It's best to add it to the Gemfile for your Rails 3 app, but if needed you can install the gem manually:
84
+
85
+ gem install chameleon
86
+
87
+ == LICENSE:
88
+
89
+ Copyright 2010 Elliott Draper <el@ejdraper.com>
90
+
91
+ Permission is hereby granted, free of charge, to any person obtaining
92
+ a copy of this software and associated documentation files (the
93
+ "Software"), to deal in the Software without restriction, including
94
+ without limitation the rights to use, copy, modify, merge, publish,
95
+ distribute, sublicense, and/or sell copies of the Software, and to
96
+ permit persons to whom the Software is furnished to do so, subject to
97
+ the following conditions:
98
+
99
+ The above copyright notice and this permission notice shall be
100
+ included in all copies or substantial portions of the Software.
101
+
102
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
103
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
104
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
105
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
106
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
107
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
108
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,20 @@
1
+ class WidgetsController < ApplicationController
2
+ before_filter :find_widget
3
+ before_filter :validate_key
4
+ skip_before_filter :verify_authenticity_token
5
+
6
+ def show
7
+ @data = @widget.data.call
8
+ render "#{@widget.type}.xml"
9
+ end
10
+
11
+ protected
12
+ def find_widget
13
+ @widget = Widget.find(params[:id].gsub(".xml", ""))
14
+ raise "Invalid widget!" if @widget.nil?
15
+ end
16
+
17
+ def validate_key
18
+ raise "Invalid key!" if !@widget.public && (params[@widget.key_parameter].blank? || params[@widget.key_parameter] != @widget.key)
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ class Widget
2
+ cattr_accessor :widgets
3
+
4
+ def self.widget(name, &block)
5
+ @@widgets ||= {}
6
+ @@widgets[name] = Widget.new(name, &block)
7
+ end
8
+
9
+ def self.find(name)
10
+ Dir.glob(File.join("app", "widgets", "*.rb")).each { |f| load File.expand_path(f) } if Rails.env == "development"
11
+ (@@widgets || {})[name.to_sym]
12
+ end
13
+
14
+ def initialize(name, &block)
15
+ @name = name
16
+ instance_eval(&block)
17
+ end
18
+
19
+ def name
20
+ @name
21
+ end
22
+
23
+ def key(value = nil)
24
+ @key = value unless value.nil?
25
+ @key
26
+ end
27
+
28
+ def key_parameter(value = nil)
29
+ @key_parameter = value unless value.nil?
30
+ @key_parameter || :key
31
+ end
32
+
33
+ def type(value = nil)
34
+ @type = value unless value.nil?
35
+ @type
36
+ end
37
+
38
+ def public(value = nil)
39
+ @public = value unless value.nil?
40
+ @public || false
41
+ end
42
+
43
+ def data(&block)
44
+ @data = block if block_given?
45
+ @data
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <root>
3
+ <item>
4
+ <value><%= @data.is_a?(Array) ? @data.first : @data %></value>
5
+ <text></text>
6
+ </item>
7
+ <item>
8
+ <value><%= @data.is_a?(Array) && @data.length == 2 ? @data.last : nil %></value>
9
+ <text></text>
10
+ </item>
11
+ </root>
@@ -0,0 +1,3 @@
1
+ Rails::Application.routes.draw do
2
+ match "widgets/:id", :to => "widgets#show"
3
+ end
@@ -0,0 +1,14 @@
1
+ module Chameleon
2
+ class Engine < Rails::Engine
3
+ engine_name :chameleon
4
+ initializer "chameleon.initialization" do
5
+ Dir.glob(File.join("app", "widgets", "*.rb")).each { |f| require File.expand_path(f) }
6
+ end
7
+ end
8
+ end
9
+
10
+ Kernel.class_eval do
11
+ def widget(name, &block)
12
+ Widget.widget(name, &block)
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ require "rails/generators"
2
+
3
+ module Chameleon
4
+ class WidgetGenerator < Rails::Generators::Base
5
+ desc "Generates a widget for exposing data to Geckoboard"
6
+ argument :name, :type => :string, :desc => "Name of the widget", :required => true
7
+ argument :type, :type => :string, :desc => "Type of the widget", :required => true
8
+
9
+ def create_widget
10
+ key = Digest::SHA1.hexdigest("#{self.name}#{self.type}#{Time.now.to_i}#{ENV["USER"]}")
11
+ data =<<EOF
12
+ widget :#{self.name.underscore} do
13
+ key "#{key}"
14
+ type "#{self.type}"
15
+ data do
16
+ end
17
+ end
18
+ EOF
19
+ create_file("app/widgets/#{self.name.underscore}_widget.rb", data)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class ChameleonTest < ActiveSupport::TestCase
4
+ test "truth" do
5
+ assert_kind_of Module, Chameleon
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+ require 'rake'
6
+
7
+ Dummy::Application.load_tasks
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= stylesheet_link_tag :all %>
6
+ <%= javascript_include_tag :defaults %>
7
+ <%= csrf_meta_tag %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,7 @@
1
+ widget :test do
2
+ key "c6fdeae2af2c327cac35abc2ccacd8619af56821"
3
+ type "number_and_secondary"
4
+ data do
5
+ [1,2]
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ widget :test2 do
2
+ key "c6fdeae2af2c327cac35abc2ccacd8619af56821"
3
+ key_parameter :token
4
+ type "number_and_secondary"
5
+ data do
6
+ [1,2]
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Dummy::Application
@@ -0,0 +1,45 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require "active_model/railtie"
4
+ require "active_record/railtie"
5
+ require "action_controller/railtie"
6
+ require "action_view/railtie"
7
+ require "action_mailer/railtie"
8
+
9
+ Bundler.require
10
+ require "chameleon"
11
+
12
+ module Dummy
13
+ class Application < Rails::Application
14
+ # Settings in config/environments/* take precedence over those specified here.
15
+ # Application configuration should go into files in config/initializers
16
+ # -- all .rb files in that directory are automatically loaded.
17
+
18
+ # Custom directories with classes and modules you want to be autoloadable.
19
+ # config.autoload_paths += %W(#{config.root}/extras)
20
+
21
+ # Only load the plugins named here, in the order given (default is alphabetical).
22
+ # :all can be used as a placeholder for all plugins not explicitly named.
23
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24
+
25
+ # Activate observers that should always be running.
26
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
27
+
28
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30
+ # config.time_zone = 'Central Time (US & Canada)'
31
+
32
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34
+ # config.i18n.default_locale = :de
35
+
36
+ # JavaScript files you want as :defaults (application.js is always included).
37
+ # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
38
+
39
+ # Configure the default encoding used in templates for Ruby 1.9.
40
+ config.encoding = "utf-8"
41
+
42
+ # Configure sensitive parameters which will be filtered from the log file.
43
+ config.filter_parameters += [:password]
44
+ end
45
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ gemfile = File.expand_path('../../../../Gemfile', __FILE__)
3
+
4
+ if File.exist?(gemfile)
5
+ ENV['BUNDLE_GEMFILE'] = gemfile
6
+ require 'bundler'
7
+ Bundler.setup
8
+ end
9
+
10
+ $:.unshift File.expand_path('../../../../lib', __FILE__)
@@ -0,0 +1,22 @@
1
+ # SQLite version 3.x
2
+ # gem install sqlite3-ruby (not necessary on OS X Leopard)
3
+ development:
4
+ adapter: sqlite3
5
+ database: db/development.sqlite3
6
+ pool: 5
7
+ timeout: 5000
8
+
9
+ # Warning: The database defined as "test" will be erased and
10
+ # re-generated from your development database when you run "rake".
11
+ # Do not set this db to the same as development or production.
12
+ test:
13
+ adapter: sqlite3
14
+ database: db/test.sqlite3
15
+ pool: 5
16
+ timeout: 5000
17
+
18
+ production:
19
+ adapter: sqlite3
20
+ database: db/production.sqlite3
21
+ pool: 5
22
+ timeout: 5000
@@ -0,0 +1,5 @@
1
+ # Load the rails application
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the rails application
5
+ Dummy::Application.initialize!
@@ -0,0 +1,26 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # In the development environment your application's code is reloaded on
5
+ # every request. This slows down response time but is perfect for development
6
+ # since you don't have to restart the webserver when you make code changes.
7
+ config.cache_classes = false
8
+
9
+ # Log error messages when you accidentally call methods on nil.
10
+ config.whiny_nils = true
11
+
12
+ # Show full error reports and disable caching
13
+ config.consider_all_requests_local = true
14
+ config.action_view.debug_rjs = true
15
+ config.action_controller.perform_caching = false
16
+
17
+ # Don't care if the mailer can't send
18
+ config.action_mailer.raise_delivery_errors = false
19
+
20
+ # Print deprecation notices to the Rails logger
21
+ config.active_support.deprecation = :log
22
+
23
+ # Only use best-standards-support built into browsers
24
+ config.action_dispatch.best_standards_support = :builtin
25
+ end
26
+
@@ -0,0 +1,49 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # The production environment is meant for finished, "live" apps.
5
+ # Code is not reloaded between requests
6
+ config.cache_classes = true
7
+
8
+ # Full error reports are disabled and caching is turned on
9
+ config.consider_all_requests_local = false
10
+ config.action_controller.perform_caching = true
11
+
12
+ # Specifies the header that your server uses for sending files
13
+ config.action_dispatch.x_sendfile_header = "X-Sendfile"
14
+
15
+ # For nginx:
16
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
17
+
18
+ # If you have no front-end server that supports something like X-Sendfile,
19
+ # just comment this out and Rails will serve the files
20
+
21
+ # See everything in the log (default is :info)
22
+ # config.log_level = :debug
23
+
24
+ # Use a different logger for distributed setups
25
+ # config.logger = SyslogLogger.new
26
+
27
+ # Use a different cache store in production
28
+ # config.cache_store = :mem_cache_store
29
+
30
+ # Disable Rails's static asset server
31
+ # In production, Apache or nginx will already do this
32
+ config.serve_static_assets = false
33
+
34
+ # Enable serving of images, stylesheets, and javascripts from an asset server
35
+ # config.action_controller.asset_host = "http://assets.example.com"
36
+
37
+ # Disable delivery errors, bad email addresses will be ignored
38
+ # config.action_mailer.raise_delivery_errors = false
39
+
40
+ # Enable threaded mode
41
+ # config.threadsafe!
42
+
43
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
44
+ # the I18n.default_locale when a translation can not be found)
45
+ config.i18n.fallbacks = true
46
+
47
+ # Send deprecation notices to registered listeners
48
+ config.active_support.deprecation = :notify
49
+ end