Liu 的个人资料唯有仰望是真实的照片日志列表更多 ![]() | 帮助 |
之前一个文章的续(zz)
2007/5/29 19 Rails Tricks Most Rails Coders Don't Know(zz)Post by Peter Cooper Permanent Link | When looking at my own Rails code and that of the community as a whole, I often see places where certain Rails techniques could have been used, but weren't. As much for my own memory as yours, I thought I'd list down some Rails tricks and tips that can make your application or code more efficient: Benchmark logic in your controller actions - It's really easy. Use the benchmark class method available on all your models like this: User.benchmark("adding and deleting 1000 users") do
1000.times do
User.create(:name => 'something')
x = User.find_by_name('something')
x.destroy
end
end
Of course, your code would be a lot better ;-) The regular SQL logs are not shown when within the benchmark sections. Only the benchmark results are shown.
Easier collections with to_proc - Sick of writing things like post.collect { |p| p.title } or post.select { |p| p.activated? }.collect{ |p| p.title} ? A little Ruby hackery that allows you to convert symbols into proc references makes it easy. You can write post.collect(&:title) or post.select(&:activated?).collect(&:title) instead! Learn a lot more about this. Convert arrays to sentences in views - If you were collecting a bunch of names to be shown in a view, you might end up with an array like ['Peter', 'Fred', 'Chris'], and joining these with commas and inserting 'and' before the final one is a common pain. Not so, if you use the array method to_sentence as provided in Rails. names.to_sentence would return Peter, Fred, and Chris. Send files back to the user - Usually, static files can be retrieved by using the direct URL and circumventing your Rails application. In some situations, however, it can be useful to hide the true location of files, particularly if you're sending something of value (e-books, for example). It may be essential to only send files to logged in users too. send_file makes it possible. It sends files in 4096 byte chunks, so even large files can be sent without slowing the system down. Iterating through page elements with RJS - Changing page elements with RJS is easy, but what if you don't know exactly which elements you want to change, and would instead prefer to address them with CSS queries? You can with RJS's select method. For example: page.select('#items li').each { |item| item.hide } . Powerful stuff! Check for existence - When doing a Model.find(id), an exception can be returned if the item with an id of 'id' doesn't exist. If you want to avoid this, use Model.exists?(id) first to get a true or false for whether that item exists or not. Number helpers for common number tasks - All of these number helpers aren't commonly used but provide great shortcuts: number_to_currency(1234567.948) # => $1,234,567.95 or human_size(1234567890) # => 1.1GB or number_with_delimiter(999999999) # => 999,999,999. There are others. Testing different route configurations easily - with_routing is a test helper that allows you to temporarily override the default 'routes' in routes.rb for test purposes. Demonstration: with_routing do |set|
set.draw { set.connect ':controller/:id/:action' }
assert_equal(
['/content/10/show', {}],
set.generate(:controller => 'content', :id => 10, :action => 'show')
)
end
You can learn a little more here. Get lots of info about requests - Checking request.post? and request.xhr? are popular ways to look for POST and AJAX requests, but some of the other request methods are lesser used. For example: request.subdomains can return an array of subdomains that you could use as part of your authentication scheme, request.request_uri returns the full local request URL, request.host returns the full hostname, request.method returns the HTTP method as a lowercase symbol, and request.ssl? returns true if it's an HTTPS / SSL request. Improving session performance even more than with ActiveRecord - By default, Rails stores sessions on the local file system. Many users change this to using ActiveRecordStore to store sessions in the database. An even faster alternative is to use Memcached to store sessions, but that takes a lot to set up (and isn't available unless you run your own servers, etc). But you can get faster than ActiveRecordStore by using Stefan Kaes' SQLSessionStore. It circumvents the inefficiencies of ActiveRecordStore using his own direct SQL technique to store sessions. Caching unchanging data at application startup - If you have data that doesn't change between application restarts, cache it in a constant somewhere. For example, you might have a YAML or XML file in /config that stores application configuration data, and you could load it into a constant in environment.rb, making lookups quick and easy application-wide. Check your views are rendering valid HTML / XHTML - It's not for everyone, but if your output validates as correct HTML / XHTML, it's a sign your views are going to render properly. Scott Raymond has developed a assert_valid_markup test helper that you can use from your functional tests. Cleaner HTML output testing - Combine why's Hpricot HTML parser and a special test extension, and you can have powerful tests like so: assert_equal "My title", tag('title') or assert element('body').should_contain('something'). This might be ideal for developing tests to test user built templates. In any case, it's nicer than assert_tag! Run long-running tasks separately in the background - BackgrounDRb is a small framework, by Ezra Zygmuntowicz, that runs as a daemon in the background that can accept tasks your Rails application sends to it, and whose execution is totally separate to your Rails app. It's extremely powerful, and useful for many tasks such as sending hundreds of e-mails, fetching URLs, and other things you don't want to slow down the request times for your main app. One great demo is to develop a task that increments a variable by 1 and sleeps for 1 second. You can then make a Rails method that queries the variable, and see the distinct separation. Learn more. Make ids in URLs more user friendly - Override the to_param method on your model and return something like "#{id}-#{title.gsub(/[^a-z0-9]+/i, '-')}" to get URLs like so: http://yoursite.com/posts/show/123-post-title-goes-here .. Much nicer for users, and you don't need to change anything with Post.find(params[:id]) as the non numeric characters will be stripped automagically! Get a full explanation here. Separate out slices of functionality into Engines - Everyone's heard of Rails' plugins, but pitifully few are using Rails Engines! Rails Engines are like plugins on steroids. They can contain their own models, controllers, and views, and integrate with any applications you run them under. This allows you to split out common fragments of functionality (login, user management, content management, etc.) into separate 'engines' to use in your different projects within minutes. No more writing dull login code! Rails Engines is a big deal, but it should be a far bigger deal. Calculations - Do you want to get maximums, minimums, averages, or sums for data in your tables? ActiveRecord's Calculations make these all possible. Person.average('age'), Person.maximum(:age, :group => 'last_name'), and Order.sum('total') all become a reality. Most can be customized pretty deeply with extra options, so go read about them if they're not already part of your code. XML or YAML output of your data - It's not necessarily to create a Builder .rxml template for all XML output. ActiveRecord has a to_xml method that will output the object or result set in XML format. It works with simple objects, to complete tables (like User.find(:all).to_xml). Using includes works too, as with Post.find(:all, :include => [:comments]).to_xml. YAML is also supported, by using to_yaml instead. 2007/5/28 太土鳖了ft啊,原来是这样写 os.path.join("home", "ayuer", "test", "whatever") 而不是 os.path.join(os.path.join(os.path.join("home", "ayuer"), "test"), "whatever") 不注重细节的毛病,我反省啊反省... Introduction to BackgrounDRb(zz)http://www.infoq.com/articles/BackgrounDRb Posted by Ezra Zygmuntowicz on Jun 27, 2006 03:09 AM
The BackgrounDRb server works by publishing a MiddleMan object. This object is the manager for your worker classes. It holds a @jobs hash composed of { job_key => running_worker_object } pairs and a @timestamps hash composed of { job_key => timestamp } pairs. The MiddleMan object straddles the interface between the DRb server and your Rails application. Here is a simple diagram to show the architecture.
This is a generic worker class as created by the worker generator provided by the plugin. $ script/generate worker Foo When your FooWorker object is instantiated from rails via MiddleMan, the do_work method is automatically run in its own thread. We use a thread here so rails does not wait for the do_work method to finish before it continues on. With BackgrounDRb, you usually create a new worker object with an AJAX request. Your view can then use periodically_call_remote to fetch the progress of your job and display it however you like. Let's flesh out the FooWorker class and show how you would create a new FooWorker object and retrieve its progress from within a rails controller. class FooWorker < BackgrounDRb::Rails Now in the controller: class MyController < ApplicationController And in your start_background_task.rhtml view file you could use something like this: <html>
<head>
<style type="text/css">
.progress{
width: 1px;
height: 16px;
color: white;
font-size: 12px;
overflow: hidden;
background-color: #287B7E;
padding-left: 5px;
}
</style>
<script type="text/javascript">
function progressPercent(bar, percentage) {
document.getElementById(bar).style.width = parseInt(percentage*2)+"px";
document.getElementById(bar).innerHTML= "<div align='center'>"+percentage+"%</div>"
}
</script>
</head>
<body>
<div id='progressbar' class="progress"></div>
<%= periodically_call_remote(:url => {:action => 'get_progress'}, :frequency => 1) %>
</body>
</html>
MiddleMan.new_worker returns a randomly generated job_key that you can store in the session for later retrieval. If you want to specify a named key instead of using the generated key you can do so like this: # This will throw a BackgrounDRbDuplicateKeyError if the :job_key already exists.
MiddleMan.new_worker(:class => :foo_worker,
:job_key => :my_worker,
:args => "Arguments used to instantiate a new FooWorker object")
MiddleMan.get_worker :my_worker
Upon instalation, the plugin writes a config file into RAILS_ROOT/config/backgroundrb.yml. In this file there is a load_rails config option. If this is set to true then you will be able to use your ActiveRecord objects in your worker classes. When you start the server it will use your already existing database.yml file for database connection details. This plugin can also be used for caching large or compute-intensive objects including ActiveRecord objects. You can store rendered views or large queries in the cache. In fact you can store any text or object that can be marshalled. Here is how you would use the cache: # Fill the cache
@posts = Post.find(:all, :include => :comments)
MiddleMan.cache_as(:post_cache, @posts)
# OR
@posts = MiddleMan.cache_as :post_cache do
Post.find(:all, :include => :comments)
end
# Retrieve the cache
@posts = MiddleMan.cache_get(:post_cache)
# OR
@posts = MiddleMan.cache_get(:post_cache) { Post.find(:all, :include => :comments) }
MiddleMan.cache_get takes an optional block argument. If the cache located at the :post_cache key is empty, the results of evaluating the block are placed in the cache and assigned to @posts. If you don't supply a block and the cache is empty it will return nil. In the current implementation, you are responsible for expiring your own caches and deleting your own workers from the main pool. This works two ways. You can either explicitly call MiddleMan.delete_worker(:job_key) or MiddleMan.delete_cache(:cache_key). There is also a MiddleMan.gc! method that takes a Time object and deletes all jobs with a time-stamp older than the one specified. Here is a script that can be run from cron to expire jobs older than 30 minutes: #!/usr/bin/env ruby require "drb" DRb.start_service MiddleMan = DRbObject.new(nil, "druby://localhost:22222") MiddleMan.gc!(Time.now - 60*30) In the near future there will be a timing mechanism built into BackgrounDRb. This will allow for jobs and garbage collection to be run at scheduled times and for specifying a time-to-live parameter when you create new jobs or caches. There are Rake tasks as well as plain Ruby command line scripts to start and stop the daemon. On OS X, linux or BSD you can use the Rake tasks to start and stop the server: $ rake backgroundrb:start $ rake backgroundrb:stop On Windows you currently have to keep a console window open while you run the backgroundrb server (Hopefully this will change in the near future). So on Windows, to start the daemon you would open a console and run the command like this: > ruby script\backgroundrb\start # ctrl-break to stop So what are a few real world use cases, you ask? Here is a small list of things I am currently using BackgrounDRb for:
Plans for the future include the ability to fork new processes to handle larger jobs that require their own Ruby interpreter instance. Also work needs to be done to let BackgrounDRb run as a Windows service. Anyone who is familiar with Windows services that can offer some help here would be greatly appreciated. Suggestions and patches are also welcome.
Date and Time Representation in Python(zz)Jochen Voss (last update: 2007-05-27) There are many different ways to represent date and time in Python programs. This page gives an overview over the different methods and explains how to convert between different representations. The main focus of this page is on how to represent points in time often assuming some fixed, local time zone. This is used for example when analysing log files. I will not explain here how to convert between different time zones or between different calendars. Date and Time representationsISO 8601 Time RepresentationThe international standard ISO 8601 describes a string representation for dates and times. Two simple examples of this format are 2007-03-04 20:32:17 20070304T203217 (which both stand for the 4th of March 2007, a bit after half past eight) but the format also allows for sub-second resolution times and to specify time zones. This format is of course not Python specific, but it is good for storing dates and times in a portable format. Details about this format can be found on Markus Kuhn's ISO 8601 page and on Gary Houston's ISO 8601:1988 Date/Time Representations page. I recommend use of this format to store times in files. One way to get the current time in this representation is to use >>> from time import strftime
>>> strftime("%Y-%m-%d %H:%M:%S")
'2007-03-03 22:14:39'
Python
|
|
|