Today logging broke in my environment ( Rack 1.4 & Sinatra 1.4 (GIT) & Thin 1.3.1 ). Don’t know why exactly (I refreshed all my Gems) – but somehow it got fixed by adding:
use Rack::CommonLogger
Today logging broke in my environment ( Rack 1.4 & Sinatra 1.4 (GIT) & Thin 1.3.1 ). Don’t know why exactly (I refreshed all my Gems) – but somehow it got fixed by adding:
use Rack::CommonLogger
Fairly easy. The important line here is:
proxy_buffering off;
I think this one works at least for most Rack based applications. The application itself runs on thin ( besides that, I only know Unicorn and Rainbow(s?) that have Streaming Support. Webrick won’t do that (yet))
My vhost config.
upstream stream {
server 127.0.0.1:4800;
server 127.0.0.1:4801;
server 127.0.0.1:4802;
server 127.0.0.1:4803;
server 127.0.0.1:4804;
}
server {
listen 80;
server_name stream.domain.com;
client_max_body_size 5M;
root /path/to/project/current/public;
error_log /path/to/project/shared/nginx/stream.error.log;
access_log off;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_buffering off;
proxy_redirect off;
if (-f $request_filename) {
break;
}
if (!-f $request_filename) {
proxy_pass http://stream;
break;
}
}
}
Took me a while to wrap my head around this: I tried to draw a circle around a specific coordinate (latitude & longitude) on a (google maps like) map.
My first attempt was:
EARTH_CIRCUMFERENCE / 2 ^ ZOOM_LEVEL / TILE_SIZE
which resulted in a circle whose radius was off by about 20% in my tests. ( South Germany; 500m radius ).
of course I had to factor in that this assumes we’re on the equator. To account the current latitude (for the circles’center) I modified the formula:
EARTH_CIRCUMFERENCE * cos(Latitude * PI / 180) / 2 ^ ZOOM_LEVEL / TILE_SIZE
Maybe Xcode 4.2 and its compiler change (LLVM) is the reason I couldn’t install the darn thing. This helped:
$ export CC=gcc-4.2 $ gem install memcached
RVM Installation seems pretty simple, worked flawlessly almost every time I used it on OSX. On my new production machine I couldn’t install it.
mkdir: cannot create directory `/usr/local/rvm': Permission denied
Every Tutorial or Snippet I tried looked liked this:
bash < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer )
To force RVM to your $HOME - or any other folder your user has RW privileges:
$ echo 'export rvm_prefix="$HOME"' > ~/.rvmrc $ echo 'export rvm_path="$HOME/.rvm"' >> ~/.rvmrc
Then rerun the above bash/curl line.
put in your Rakefile:
namespace :db do task :seed => :environment do seed_file = File.join(File.dirname(__FILE__), 'db', 'seeds.rb') load(seed_file) if File.exist?(seed_file) end end
namespace :db do desc "Migrate the database through scripts in db/migrate." task :migrate => :environment do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n' task :rollback => :environment do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback('db/migrate/', step) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end end
namespace :db do namespace :schema do desc "Create a db/schema.rb file that can be portably used against any DB supported by AR" task :dump => :environment do require 'active_record/schema_dumper' File.open(ENV['SCHEMA'] || "db/schema.rb", "w") do |file| ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) end end desc "Load a schema.rb file into the database" task :load => :environment do file = ENV['SCHEMA'] || "db/schema.rb" load(file) end end end
For my most recent project I created some kind of “auto-documentation”. I thought the most easy way was to simply use MySQLs comments. Turns out, ActiveRecords doesn’t support this comments at all. Neither in schema dumping nor in migrations. ( used also for recreating database from a schema.rb ). This ain’t bound to Rails; It works wherever you use ActiveRecord (well tested only with activerecord gem 3.1.1).
(found here: http://goo.gl/7JO5T)
(working with activerecord 3.1.1)
module ActiveRecord module ConnectionAdapters class ColumnDefinition attr_accessor :comment def to_sql_with_comment column_sql = to_sql_without_comment return column_sql if comment.nil? "#{column_sql} COMMENT '#{base.quote_string(comment)}'" end alias_method_chain :to_sql, :comment end class TableDefinition def column(name, type, options = {}) column = self[name] || ColumnDefinition.new(@base, name, type) if options[:limit] column.limit = options[:limit] elsif native[type.to_sym].is_a?(Hash) column.limit = native[type.to_sym][:limit] end column.precision = options[:precision] column.scale = options[:scale] column.default = options[:default] column.null = options[:null] column.comment = options[:comment] @columns << column unless @columns.include? column self end end end end
module ActiveRecord module ConnectionAdapters class MysqlColumn attr_accessor :comment end class MysqlAdapter def columns(table_name, name = nil) sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}" columns = [] result = execute(sql, name) result.each { |field| c = MysqlColumn.new(field[0], field[5], field[1], field[3] == "YES") c.comment = field[8] || "" columns << c } result.free columns end end end end
You can use that in your application wherever you need access to the comment (or any other column information)
MyModel.columns_hash.each do |a| puts a[1].type.to_s puts a[1].name.to_s puts a[1].comment if a[1].respond_to?("comment") end
to add comment to schema.rb export
module ActiveRecord class SchemaDumper def table(table, stream) columns = @connection.columns(table) begin tbl = StringIO.new if @connection.respond_to?(:pk_and_sequence_for) pk, _ = @connection.pk_and_sequence_for(table) elsif @connection.respond_to?(:primary_key) pk = @connection.primary_key(table) end tbl.print " create_table #{table.inspect}" if columns.detect { |c| c.name == pk } if pk != 'id' tbl.print %Q(, :primary_key => "#{pk}") end else tbl.print ", :id => false" end tbl.print ", :force => true" tbl.puts " do |t|" column_specs = columns.map do |column| raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil? next if column.name == pk spec = {} spec[:name] = column.name.inspect spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) } 'decimal' else column.type.to_s end spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal' spec[:precision] = column.precision.inspect if column.precision spec[:scale] = column.scale.inspect if column.scale spec[:null] = 'false' unless column.null spec[:comment] = column.comment.inspect spec[:default] = default_string(column.default) if column.has_default? (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")} spec end.compact keys = [:name, :limit, :precision, :scale, :default, :null, :comment] & column_specs.map{ |k| k.keys }.flatten lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max } format_string = lengths.map{ |len| "%-#{len}s" } type_length = column_specs.map{ |column| column[:type].length }.max format_string.unshift " t.%-#{type_length}s " format_string *= '' column_specs.each do |colspec| values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len } values.unshift colspec[:type] tbl.print((format_string % values).gsub(/,\s*$/, '')) tbl.puts end tbl.puts " end" tbl.puts indexes(table, tbl) tbl.rewind stream.print tbl.read rescue => e stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}" stream.puts "# #{e.message}" stream.puts end stream end end end
(copy this snippets somewhere to your app, everything gets done by monkey patching)
Have you ever tried to get ActiveRecord Observers working in a Sinatra environment? I spent hours crawling trough the Rails sources but eventually gave up.
Plus: My goal anyway was to have some kind of global observer through all models in my application. So, easy decision – I build my one solution – as always: with “Blackjack and Hookers”.
While browsing the ActiveRecord sources I found the perfect entrance to hook in to the needed callbacks (after_create, after_update and after_destroy).
module ObjectWatcher def self.included(base) base.after_create do |obj| puts "Yeah - we got a create" end base.after_update do |obj| puts "Yeah - we got an update" end base.after_destroy do |obj| puts "Yeah - we got a destroy" end end end [...] ActiveRecord::Base.send(:include, ObjectWatcher)
After that, the rest was pretty easy an straightforward: Create an Observer. Well: Someone has to receive above signals, and do something with them. If you only need one observer, you easily could do you stuff in the blocks. I’d like to be more flexible and for now, I have already 5 observers listening.
this one gets called by the ObjectWatcher and dispatches to all its listeners (observers)
class ObjectListener class << self def listeners @listeners ||= [] end def listeners=(l) @listeners = l end def run(obj, action) mn = "after_#{action}" self.listeners.each do |listener| lo = listener.to_s.classify.constantize lo.send mn, obj if lo.respond_to?(mn) end end end end
Usage in ObjectWatcher:
[...] base.after_create do |obj| ObjectListener.run obj, :create end [...]
class TestObserver def self.after_update(record) puts " >> updated" end def self.after_create(record) puts " >> created" end def self.after_destroy(record) puts " >> destroyed" end end [...] ObjectListener.listeners << TestObserver
that’s it – happy coding.
( Update: 19.04.2012 – Fixed errors )