155 karma · joined July 15, 2011
Note that it doesn't prevent or discourage monkey-patching, it just requires that you apply your monkey-patches before freezing the core classes. The idea here is similar to OpenBSD's pledge, where startup and runtime are considered different phases of an application's lifecycle. After startup, you limit what the runtime can do. With refrigerator, you freeze the core classes at the end of startup. So monkey patches are allowed during startup, but not at runtime.
PostgreSQL's "timestamp with time zone" doesn't store timezone, it converts the time to UTC and stores that, and on retrieval converts the value to the connection's time zone.
Considering that password hashes are stored in the users table, it seems unlikely. While you can use PostgreSQL to implement per-column permissions, it's a fairly large pain, and you have to make sure every query you are using that selects from the table does not select that column. Rails/ActiveRecord by default selects all columns in the model's table, and it's a fair amount of work to work around that.
If you want an example for a Ruby authentication library that does this, there is Rodauth: https://github.com/jeremyevans/rodauth
Passwords appear to be stored in the users table in the "encrypted_password" column, and it does not appear that any database-based security is used. This is one RCE/SQLI vulnerability away from exposing the password hashes for all users. To be fair, that's probably true of most sites that store password hashes, but I would have expected better from 18F.
Post.where(id: 1).or(Sequel.&({:id=>2}, {:name=>'Foo'}))
# SELECT * FROM posts WHERE ((id = 1) OR ((id = 2) AND (name = 'Foo'))) Post.where(id: 1).or Post.joins(:author).where(author: { name: 'John' })
ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:joins, :references]
It appears that only the filter clauses are allowed to be different (similar errors if :select, :order, :group, :limit are different), in which case it's no more powerful than Sequel, just more of a pain to use. You can easily implement ActiveRecord's behavior in Sequel if you want to combine filter clauses for arbitrary datasets: ds = Post.where(id: 2)
Post.where(id: 1).or(ds.opts[:where])
It's also interesting what happens if you mix where and having clauses (I'm not saying it doesn't make sense, but it may bite someone): Post.where(id: 1).or(Post.having(id: 2)).to_sql
# SELECT "posts".* FROM "posts"Your workflow with Rack->Ramaze should work similarly when using Roda. Roda scales down well:
# config.ru
require 'roda'
Roda.route{'Hello world'}
run Roda
There is probably some level of complexity where Roda may not be a good choice. I'm not sure what that level is, but I don't expect to hit it on sites I work on. In any case it's probably best to split up such an app into multiple apps/services before that level is reached.In terms of battle readiness, it's new and currently I think I'm the only person using it in production. I hesitate to call something battle ready until there are more people using it, but I'm very committed to it and you can expect the same level of support for Roda that Sequel receives.
Thanks for the tip on embedded gists, I'll look into that.
There are certainly parts of Sequel that are overly clever, I won't argue that. But compared to similar libraries, I don't think it's worse in that department.
I've converted about 15 apps from Sinatra to Roda, and a couple of Rails apps to Roda (working on converting my final Rails app). Personally, I've found that the biggest advantages come when the URL structure mirrors your application structure, and you have redundant code in many routes, as that type of code becomes simpler, faster, and DRYer with Roda.
Yes, you can use a before block with wildcard routes with Sinatra, but Sinatra will still traverse the routing list sequentially and recheck the full route for every entry in the list. Unfortunately, while I love Sinatra, have contributed patches to it, and have used it since 0.1.0, the approach doesn't scale well for sites with a lot of routes.
The code non-locality issue shouldn't be ignored, and is probably the largest issue with Roda, but the issue is probably going to happen with any approach that DRYs up the code to the same degree. It's true if you use a before block in Sinatra, or a before_filter in Rails.
The use of a routing tree really isn't an innovative approach (at least not innovated by me). It's been around in Ruby since Rum was released in 2008 (and maybe earlier elsewhere). However, it's not well known, and I hope that Roda brings it into the mainstream.
One of the best things about Roda that I don't think has been mentioned in the comments yet is the plugin system, which I borrowed from Sequel. This makes it easy to extend Roda beyond its very small core. For example, the multi_route plugin makes it easy to scale Roda to larger sites by splitting up routing subtrees into specific files. There are currently about 15 plugins, and I have ideas for quite a few more that I will implement after finishing up my current Rails->Roda conversion.
I apologize that the code on the website isn't syntax highlighted. If anyone can offer their design skills, I would greatly appreciate it.
If you have any questions about Roda, please ask.
While mostly accurate, his description of the module hierachy as a tree leads to a wrong understanding of how ruby's method lookup actually works. If ruby did use a tree, then including module B in module A after including module A in class C would result B being one of C's ancestors, which isn't the case. Ruby's method lookup uses a linked list (not a tree) using iclasses for modules (a pseudo-copy of the module, which is why later includes have no effect). However, a description of that may be too in-depth for an introduction.