diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9dc2b51
--- /dev/null
+++ b/README.md
@@ -0,0 +1,129 @@
+# Ransack
+
+Ransack is a rewrite of [MetaSearch](http://metautonomo.us/projects/metasearch). While it
+supports many of the same features as MetaSearch, its underlying implementation differs
+greatly from MetaSearch, and _backwards compatibility is not a design goal._
+
+Ransack enables the creation of both simple and advanced search forms against your
+application's models. If you're looking for something that simplifies query generation
+at the model or controller layer, you're probably not looking for Ransack (or MetaSearch,
+for that matter). Try [Squeel](http://metautonomo.us/projects/squeel) instead.
+
+## Getting started
+
+In your Gemfile:
+
+ gem "ransack" # Last officially released gem
+ # gem "ransack", :git => "git://github.com/ernie/ransack.git" # Track git repo
+
+If you'd like to add your own custom Ransack predicates:
+
+ Ransack.configure do |config|
+ config.add_predicate 'equals_diddly', # Name your predicate
+ # What non-compound ARel predicate will it use? (eq, matches, etc)
+ :arel_predicate => 'eq',
+ # Format incoming values as you see fit. (Default: Don't do formatting)
+ :formatter => proc {|v| "#{v}-diddly"},
+ # Validate a value. An "invalid" value won't be used in a search.
+ # Below is default.
+ :validator => proc {|v| v.present?}
+ # Should compounds be created? Will use the compound (any/all) version
+ # of the arel_predicate to create a corresponding any/all version of
+ # your predicate. (Default: true)
+ :compounds => true,
+ # Force a specific column type for type-casting of supplied values.
+ # (Default: use type from DB column)
+ :type => :string
+ end
+
+## Usage
+
+Ransack can be used in one of two modes, simple or advanced.
+
+### Simple Mode
+
+This mode works much like MetaSearch, for those of you who are familiar with it, and
+requires very little setup effort.
+
+If you're coming from MetaSearch, things to note:
+
+ 1. The default param key for search params is now `:q`, instead of `:search`. This is
+ primarily to shorten query strings, though advanced queries (below) will still
+ run afoul of URL length limits in most browsers and require a switch to HTTP
+ POST requests.
+ 2. `form_for` is now `search_form_for`, and validates that a Ransack::Search object
+ is passed to it.
+ 3. Common ActiveRecord::Relation methods are no longer delegated by the search object.
+ Instead, you will get your search results (an ActiveRecord::Relation in the case of
+ the ActiveRecord adapter) via a call to `Search#result`. If passed `:distinct => true`,
+ `result` will generate a `SELECT DISTINCT` to avoid returning duplicate rows, even if
+ conditions on a join would otherwise result in some.
+
+In your controller:
+
+ def index
+ @q = Person.search(params[:q])
+ @people = @q.result(:distinct => true)
+ end
+
+In your view:
+
+ <%= search_form_for @q do |f| %>
+ <%= f.label :name_cont %>
+ <%= f.text_field :name_cont %>
+ <%= f.label :articles_title_start %>
+ <%= f.text_field :articles_title_start %>
+ <%= f.submit %>
+ <% end %>
+
+`cont` (contains) and `start` (starts with) are just two of the available search predicates.
+See Constants for a full list.
+
+### Advanced Mode
+
+"Advanced" searches (ab)use Rails' nested attributes functionality in order to generate
+complex queries with nested AND/OR groupings, etc. This takes a bit more work but can
+generate some pretty cool search interfaces that put a lot of power in the hands of
+your users. A notable drawback with these searches is that the increased size of the
+parameter string will typically force you to use the HTTP POST method instead of GET. :(
+
+This means you'll need to tweak your routes...
+
+ resources :people do
+ collection do
+ match 'search' => 'people#search', :via => [:get, :post], :as => :search
+ end
+ end
+
+... and add another controller action ...
+
+ def search
+ index
+ render :index
+ end
+
+... and update your `search_form_for` line in the view ...
+
+ <%= search_form_for @q, :url => search_people_path,
+ :html => {:method => :post} do |f| %>
+
+Once you've done so, you can make use of the helpers in Ransack::Helpers::FormBuilder to
+construct much more complex search forms.
+
+**more docs to come**
+
+## Contributions
+
+If you'd like to support the continued development of Ransack, please consider
+[making a donation](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=48Q9HY64L3TWA).
+
+To support the project in other ways:
+
+* Use Ransack in your apps, and let me know if you encounter anything that's broken or missing.
+ A failing spec is awesome. A pull request is even better!
+* Spread the word on Twitter, Facebook, and elsewhere if Ransack's been useful to you. The more
+ people who are using the project, the quicker we can find and fix bugs!
+
+## Copyright
+
+Copyright © 2011 [Ernie Miller](http://twitter.com/erniemiller)
\ No newline at end of file
diff --git a/README.rdoc b/README.rdoc
deleted file mode 100644
index 8091a1e..0000000
--- a/README.rdoc
+++ /dev/null
@@ -1,5 +0,0 @@
-= Ransack
-
-Don't use me.
-
-Seriously, I'm not anywhere close to ready for public consumption, yet.
diff --git a/lib/ransack/configuration.rb b/lib/ransack/configuration.rb
index b392c40..7db492b 100644
--- a/lib/ransack/configuration.rb
+++ b/lib/ransack/configuration.rb
@@ -7,10 +7,6 @@ module Ransack
mattr_accessor :predicates
self.predicates = {}
- def predicate_keys
- predicates.keys.sort {|a,b| b.length <=> a.length}
- end
-
def configure
yield self
end
diff --git a/lib/ransack/helpers/form_builder.rb b/lib/ransack/helpers/form_builder.rb
index 4bf6ae0..8ba901a 100644
--- a/lib/ransack/helpers/form_builder.rb
+++ b/lib/ransack/helpers/form_builder.rb
@@ -111,7 +111,7 @@ module Ransack
def predicate_select(options = {}, html_options = {})
options[:compounds] = true if options[:compounds].nil?
- keys = options[:compounds] ? Ransack.predicate_keys : Ransack.predicate_keys.reject {|k| k.match(/_(any|all)$/)}
+ keys = options[:compounds] ? Predicate.names : Predicate.names.reject {|k| k.match(/_(any|all)$/)}
if only = options[:only]
if only.respond_to? :call
keys = keys.select {|k| only.call(k)}
diff --git a/lib/ransack/nodes/condition.rb b/lib/ransack/nodes/condition.rb
index 216436d..1cfdc07 100644
--- a/lib/ransack/nodes/condition.rb
+++ b/lib/ransack/nodes/condition.rb
@@ -28,7 +28,7 @@ module Ransack
def extract_attributes_and_predicate(key)
str = key.dup
- name = Ransack.predicate_keys.detect {|p| str.sub!(/_#{p}$/, '')}
+ name = Predicate.detect_and_strip_from_string!(str)
predicate = Predicate.named(name)
raise ArgumentError, "No valid predicate for #{key}" unless predicate
attributes = str.split(/_and_|_or_/)
@@ -194,7 +194,7 @@ module Ransack
def formatted_values_for_attribute(attr)
casted_values_for_attribute(attr).map do |val|
val = attr.ransacker.formatter.call(val) if attr.ransacker && attr.ransacker.formatter
- val = predicate.formatter.call(val) if predicate.formatter
+ val = predicate.format(val)
val
end
end
diff --git a/lib/ransack/nodes/grouping.rb b/lib/ransack/nodes/grouping.rb
index b4b981f..6d15aa0 100644
--- a/lib/ransack/nodes/grouping.rb
+++ b/lib/ransack/nodes/grouping.rb
@@ -173,7 +173,7 @@ module Ransack
def strip_predicate_and_index(str)
string = str.split(/\(/).first
- Ransack.predicate_keys.detect {|p| string.sub!(/_#{p}$/, '')}
+ Predicate.detect_and_strip_from_string!(string)
string
end
diff --git a/lib/ransack/predicate.rb b/lib/ransack/predicate.rb
index 01ab23b..6651457 100644
--- a/lib/ransack/predicate.rb
+++ b/lib/ransack/predicate.rb
@@ -3,13 +3,35 @@ module Ransack
attr_reader :name, :arel_predicate, :type, :formatter, :validator, :compound
class << self
+
+ def names
+ Ransack.predicates.keys
+ end
+
+ def names_by_decreasing_length
+ names.sort {|a,b| b.length <=> a.length}
+ end
+
def named(name)
Ransack.predicates[name.to_s]
end
- def for_attribute_name(attribute_name)
- self.named(Ransack.predicate_keys.detect {|p| attribute_name.to_s.match(/_#{p}$/)})
+ def detect_and_strip_from_string!(str)
+ names_by_decreasing_length.detect {|p| str.sub!(/_#{p}$/, '')}
end
+
+ def detect_from_string(str)
+ names_by_decreasing_length.detect {|p| str.match(/_#{p}$/)}
+ end
+
+ def name_from_attribute_name(attribute_name)
+ names_by_decreasing_length.detect {|p| attribute_name.to_s.match(/_#{p}$/)}
+ end
+
+ def for_attribute_name(attribute_name)
+ self.named(detect_from_string(attribute_name.to_s))
+ end
+
end
def initialize(opts = {})
@@ -31,11 +53,19 @@ module Ransack
name.hash
end
+ def format(val)
+ if formatter
+ formatter.call(val)
+ else
+ val
+ end
+ end
+
def validate(vals)
if validator
vals.select {|v| validator.call(v.value)}.any?
else
- vals.select {|v| !v.blank?}.any?
+ vals.select {|v| v.present?}.any?
end
end
diff --git a/lib/ransack/translate.rb b/lib/ransack/translate.rb
index 2a94f54..78939a6 100644
--- a/lib/ransack/translate.rb
+++ b/lib/ransack/translate.rb
@@ -18,7 +18,7 @@ module Ransack
original_name = key.to_s
base_class = context.klass
base_ancestors = base_class.ancestors.select { |x| x.respond_to?(:model_name) }
- predicate = Ransack.predicate_keys.detect {|p| original_name.match(/_#{p}$/)}
+ predicate = Predicate.detect_from_string(original_name)
attributes_str = original_name.sub(/_#{predicate}$/, '')
attribute_names = attributes_str.split(/_and_|_or_/)
combinator = attributes_str.match(/_and_/) ? :and : :or
diff --git a/ransack.gemspec b/ransack.gemspec
index 8893e8c..cd0c96e 100644
--- a/ransack.gemspec
+++ b/ransack.gemspec
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
s.authors = ["Ernie Miller"]
s.email = ["ernie@metautonomo.us"]
s.homepage = "http://metautonomo.us/projects/ransack"
- s.summary = %q{Object-based searching. Like MetaSearch, but this time, with a better name.}
- s.description = %q{Not yet ready for public consumption.}
+ s.summary = %q{Object-based searching for ActiveRecord (currently).}
+ s.description = %q{Ransack is the successor to the MetaSearch gem. It improves and expands upon MetaSearch's functionality, but does not have a 100%-compatible API.}
s.rubyforge_project = "ransack"
diff --git a/spec/ransack/helpers/form_builder_spec.rb b/spec/ransack/helpers/form_builder_spec.rb
index 19d9c65..1a0beb5 100644
--- a/spec/ransack/helpers/form_builder_spec.rb
+++ b/spec/ransack/helpers/form_builder_spec.rb
@@ -88,28 +88,28 @@ module Ransack
it 'returns predicates with predicate_select' do
html = @f.predicate_select
- Ransack.predicate_keys.each do |key|
+ Predicate.names.each do |key|
html.should match /