From f848ae233ae3f7093de94ae2caf1fa47db574c42 Mon Sep 17 00:00:00 2001 From: Andrew Burleson Date: Sun, 3 Apr 2011 23:28:15 -0500 Subject: [PATCH 01/17] Added the ability to specify 'sort_preference' in sort links, so you can optionally have them sort in descending order first instead of ascending order. --- lib/meta_search/helpers/url_helper.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/meta_search/helpers/url_helper.rb b/lib/meta_search/helpers/url_helper.rb index b85cb44..7084f03 100644 --- a/lib/meta_search/helpers/url_helper.rb +++ b/lib/meta_search/helpers/url_helper.rb @@ -21,9 +21,18 @@ def sort_link(builder, attribute, *args) attr_name = attribute.to_s name = (args.size > 0 && !args.first.is_a?(Hash)) ? args.shift.to_s : builder.base.human_attribute_name(attr_name) prev_attr, prev_order = builder.search_attributes['meta_sort'].to_s.split('.') - current_order = prev_attr == attr_name ? prev_order : nil - new_order = current_order == 'asc' ? 'desc' : 'asc' + options = args.first.is_a?(Hash) ? args.shift : {} + current_order = prev_attr == attr_name ? prev_order : nil + + if defined?(options[:sort_preference]) && options[:sort_preference]==:desc + new_order = current_order == 'desc' ? 'asc' : 'desc' + options.delete(:sort_preference) + else + new_order = current_order == 'asc' ? 'desc' : 'asc' + options.delete(:sort_preference) + end + html_options = args.first.is_a?(Hash) ? args.shift : {} css = ['sort_link', current_order].compact.join(' ') html_options[:class] = [css, html_options[:class]].compact.join(' ') From 86b7373df92c5caf11ecc921ef66a4662ec37380 Mon Sep 17 00:00:00 2001 From: Andrew Burleson Date: Sun, 3 Apr 2011 22:19:02 -0700 Subject: [PATCH 02/17] Added instructions on using reverse sort order in sort links. --- README.rdoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rdoc b/README.rdoc index d931c36..73d37ac 100644 --- a/README.rdoc +++ b/README.rdoc @@ -314,6 +314,10 @@ Or, if in the context of a form_for against a MetaSearch::Builder: <%= f.sort_link :title %> +By default, the link that is created will sort by the given column in ascending order when first clicked. If you'd like to reverse this (so the first click sorts the results in descending order), you can pass `:sort_preference=>:desc` in the options hash, like so: + + <%= sort_link @search, :ratings, "Highest Rated", :sort_preference=>:desc %> + The @search object is the instance of MetaSearch::Builder you got back earlier from your controller. The other required parameter is the attribute name itself. Optionally, you can provide a string as a 3rd parameter to override the default link name, and then From 73d47d35fa43e379e4cfa90595009fbeddf0b1d1 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 5 May 2011 12:44:51 -0400 Subject: [PATCH 03/17] tests passing against edge rails apart from the ones dependent on https://github.com/rails/rails/pull/399 --- Rakefile | 7 +- VERSION | 2 +- lib/meta_search.rb | 2 +- lib/meta_search/builder.rb | 78 ++++++++--------- lib/meta_search/join_dependency.rb | 133 +++++++++++++++-------------- test/test_view_helpers.rb | 34 ++++++-- vendor/arel | 2 +- vendor/rails | 2 +- 8 files changed, 139 insertions(+), 121 deletions(-) diff --git a/Rakefile b/Rakefile index 5714ec5..8a8be87 100644 --- a/Rakefile +++ b/Rakefile @@ -15,10 +15,9 @@ begin gem.homepage = "http://metautonomo.us/projects/metasearch/" gem.authors = ["Ernie Miller"] gem.add_development_dependency "shoulda" - gem.add_dependency "activerecord", "~> 3.0.2" - gem.add_dependency "activesupport", "~> 3.0.2" - gem.add_dependency "actionpack", "~> 3.0.2" - gem.add_dependency "arel", "~> 2.0.2" + gem.add_dependency "activerecord", "~> 3.1.0" + gem.add_dependency "activesupport", "~> 3.1.0" + gem.add_dependency "actionpack", "~> 3.1.0" gem.post_install_message = < 1 remainder = [] found_assoc = nil @@ -252,53 +247,58 @@ def type_from_association_segments(segments, base, depth) type end - def build_or_find_association(association, parent = @join_dependency.join_base, klass = nil) + def build_or_find_association(name, parent = @join_dependency.join_base, klass = nil) found_association = @join_dependency.join_associations.detect do |assoc| - assoc.reflection.name == association.to_sym && - assoc.reflection.klass == klass && - assoc.parent == parent + assoc.reflection.name == name && + assoc.parent == parent && + (!klass || assoc.reflection.klass == klass) end unless found_association - @join_dependency.send(:build_with_metasearch, association, parent, @join_type, klass) + @join_dependency.send(:build_polymorphic, name.to_sym, parent, @join_type, klass) found_association = @join_dependency.join_associations.last + # Leverage the stashed association functionality in AR @relation = @relation.joins(found_association) end + found_association end - def build_join_dependency - joins = @relation.joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq - - association_joins = joins.select do |j| - [Hash, Array, Symbol].include?(j.class) && !array_of_strings?(j) - end - - stashed_association_joins = joins.select do |j| - j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) + def build_join_dependency(relation) + buckets = relation.joins_values.group_by do |join| + case join + when String + 'string_join' + when Hash, Symbol, Array + 'association_join' + when ::ActiveRecord::Associations::JoinDependency::JoinAssociation + 'stashed_join' + when Arel::Nodes::Join + 'join_node' + else + raise 'unknown class: %s' % join.class.name + end end - non_association_joins = (joins - association_joins - stashed_association_joins) - custom_joins = custom_join_sql(*non_association_joins) + association_joins = buckets['association_join'] || [] + stashed_association_joins = buckets['stashed_join'] || [] + join_nodes = buckets['join_node'] || [] + string_joins = (buckets['string_join'] || []).map { |x| + x.strip + }.uniq - ActiveRecord::Associations::ClassMethods::JoinDependency.new(@base, association_joins, custom_joins) - end + join_list = relation.send :custom_join_ast, relation.table.from(relation.table), string_joins - def custom_join_sql(*joins) - arel = @relation.table - joins.each do |join| - next if join.blank? + join_dependency = ::ActiveRecord::Associations::JoinDependency.new( + relation.klass, + association_joins, + join_list + ) - case join - when Hash, Array, Symbol - if array_of_strings?(join) - join_string = join.join(' ') - arel = arel.join(join_string) - end - else - arel = arel.join(join) - end + join_nodes.each do |join| + join_dependency.alias_tracker.aliased_name_for(join.left.name.downcase) end - arel.joins(arel) + + join_dependency.graft(*stashed_association_joins) end def get_join_type(opt_join) diff --git a/lib/meta_search/join_dependency.rb b/lib/meta_search/join_dependency.rb index 72e6f82..02edfbf 100644 --- a/lib/meta_search/join_dependency.rb +++ b/lib/meta_search/join_dependency.rb @@ -2,90 +2,93 @@ module MetaSearch module JoinDependency + class JoinAssociation < ::ActiveRecord::Associations::JoinDependency::JoinAssociation + + def initialize(reflection, join_dependency, parent = nil, polymorphic_class = nil) + if polymorphic_class && ::ActiveRecord::Base > polymorphic_class + swapping_reflection_klass(reflection, polymorphic_class) do |reflection| + super(reflection, join_dependency, parent) + end + else + super(reflection, join_dependency, parent) + end + end + + def swapping_reflection_klass(reflection, klass) + reflection = reflection.clone + original_polymorphic = reflection.options.delete(:polymorphic) + reflection.instance_variable_set(:@klass, klass) + yield reflection + ensure + reflection.options[:polymorphic] = original_polymorphic + end + + def ==(other) + super && active_record == other.active_record + end + + def build_constraint(reflection, table, key, foreign_table, foreign_key) + if reflection.options[:polymorphic] + super.and( + foreign_table[reflection.foreign_type].eq(reflection.klass.name) + ) + else + super + end + end + + end + + # Yes, I'm using alias_method_chain here. No, I don't feel too + # bad about it. JoinDependency, or, to call it by its full proper + # name, ::ActiveRecord::Associations::JoinDependency, is one of the + # most "for internal use only" chunks of ActiveRecord. def self.included(base) base.class_eval do - alias_method_chain :graft, :metasearch + alias_method_chain :graft, :meta_search end end - def graft_with_metasearch(*associations) + def graft_with_meta_search(*associations) associations.each do |association| join_associations.detect {|a| association == a} || - ( - association.class == MetaSearch::PolymorphicJoinAssociation ? - build_with_metasearch(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type, association.reflection.klass) : - build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type) - ) + build_polymorphic(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type, association.reflection.klass) end self end - protected + # Should only be called by MetaSearch, and only with a single association name + def build_polymorphic(association, parent = nil, join_type = Arel::OuterJoin, klass = nil) + parent ||= join_parts.last + reflection = parent.reflections[association] or + raise ::ActiveRecord::ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" + unless join_association = find_join_association_respecting_polymorphism(reflection, parent, klass) + @reflections << reflection + join_association = build_join_association_respecting_polymorphism(reflection, parent, klass) + join_association.join_type = join_type + @join_parts << join_association + cache_joined_association(join_association) + end - def build_with_metasearch(associations, parent = nil, join_type = Arel::Nodes::InnerJoin, polymorphic_class = nil) - parent ||= @joins.last - case associations - when Symbol, String - reflection = parent.reflections[associations.to_s.intern] or - raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?" - unless (association = find_join_association(reflection, parent)) && (!polymorphic_class || association.active_record == polymorphic_class) - @reflections << reflection - if reflection.options[:polymorphic] - raise ArgumentError, "You can't create a polymorphic belongs_to join without specifying the polymorphic class!" unless polymorphic_class - association = PolymorphicJoinAssociation.new(reflection, self, polymorphic_class, parent) - else - association = build_join_association(reflection, parent) - end - association.join_type = join_type - @joins << association - end + join_association + end + + def find_join_association_respecting_polymorphism(reflection, parent, klass) + if association = find_join_association(reflection, parent) + unless reflection.options[:polymorphic] association else - build(associations, parent, join_type) + association if association.active_record == klass end end - end - - class PolymorphicJoinAssociation < ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation - - def initialize(reflection, join_dependency, polymorphic_class, parent = nil) - reflection.check_validity! - @active_record = polymorphic_class - @cached_record = {} - @join_dependency = join_dependency - @parent = parent || join_dependency.join_base - @reflection = reflection.clone - @reflection.instance_variable_set(:"@klass", polymorphic_class) - @aliased_prefix = "t#{ join_dependency.joins.size }" - @parent_table_name = @parent.active_record.table_name - @aliased_table_name = aliased_table_name_for(table_name) - @join = nil - @join_type = Arel::Nodes::InnerJoin - end - - def ==(other) - other.class == self.class && - other.reflection == reflection && - other.active_record == active_record && - other.parent == parent end - def association_join - return @join if @join - - aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine) - parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine) - - @join = [ - aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name]), - parent_table[options[:foreign_type]].eq(active_record.name) - ] - - if options[:conditions] - @join << interpolate_sql(sanitize_sql(options[:conditions], aliased_table_name)) + def build_join_association_respecting_polymorphism(reflection, parent, klass = nil) + if reflection.options[:polymorphic] && klass + JoinAssociation.new(reflection, self, parent, klass) + else + JoinAssociation.new(reflection, self, parent) end - - @join end end end \ No newline at end of file diff --git a/test/test_view_helpers.rb b/test/test_view_helpers.rb index 8596f71..1bcf456 100644 --- a/test/test_view_helpers.rb +++ b/test/test_view_helpers.rb @@ -6,18 +6,34 @@ class TestViewHelpers < ActionView::TestCase tests MetaSearch::Helpers::FormHelper include MetaSearch::Helpers::UrlHelper - router = ActionDispatch::Routing::RouteSet.new - router.draw do - resources :developers - resources :companies - resources :projects - resources :notes - match ':controller(/:action(/:id(.:format)))' + def self.router + @router ||= begin + router = ActionDispatch::Routing::RouteSet.new + router.draw do + resources :developers + resources :companies + resources :projects + resources :notes + match ':controller(/:action(/:id(.:format)))' + end + router + end end + include router.url_helpers + # FIXME: figure out a cleaner way to get this behavior def setup + router = self.class.router @controller = ActionView::TestCase::TestController.new + @controller.instance_variable_set(:@_routes, router) + @controller.class_eval do + include router.url_helpers + end + + @controller.view_context_class.class_eval do + include router.url_helpers + end end context "A search against Company and a search against Developer" do @@ -326,7 +342,7 @@ def setup end should "maintain previous search options in its sort links" do - assert_match /search\[name_contains\]=a/, + assert_match /search%5Bname_contains%5D=a/, sort_link(@s, :name, :controller => 'companies') end end @@ -359,7 +375,7 @@ def setup end should "maintain previous search options in its sort links" do - assert_match /search\[name_contains\]=a/, + assert_match /search%5Bname_contains%5D=a/, sort_link(@s, :company_name, :controller => 'companies') end end diff --git a/vendor/arel b/vendor/arel index c0b1f9e..a3d9c39 160000 --- a/vendor/arel +++ b/vendor/arel @@ -1 +1 @@ -Subproject commit c0b1f9e40550f396dd69d4f094670b9c0e076149 +Subproject commit a3d9c393b4707a03734dcf6831fb9636fabac045 diff --git a/vendor/rails b/vendor/rails index 1081ea6..ee898e5 160000 --- a/vendor/rails +++ b/vendor/rails @@ -1 +1 @@ -Subproject commit 1081ea66a3d864f310a1a55f22229af4e10436a7 +Subproject commit ee898e5248d3876372c8303f500afe2baf6f298c From bce0c82f192ef9f755975eb3501547f3d810d59e Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 5 May 2011 12:46:54 -0400 Subject: [PATCH 04/17] Update gemspec --- meta_search.gemspec | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/meta_search.gemspec b/meta_search.gemspec index 1b9b268..d28527b 100644 --- a/meta_search.gemspec +++ b/meta_search.gemspec @@ -5,9 +5,9 @@ Gem::Specification.new do |s| s.name = %q{meta_search} - s.version = "1.0.5" + s.version = "1.1.0.pre" - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = [%q{Ernie Miller}] s.date = %q{2011-05-05} s.description = %q{ @@ -79,23 +79,20 @@ you're feeling especially appreciative. It'd help me justify this if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, ["~> 3.0.2"]) - s.add_runtime_dependency(%q, ["~> 3.0.2"]) - s.add_runtime_dependency(%q, ["~> 3.0.2"]) - s.add_runtime_dependency(%q, ["~> 2.0.2"]) + s.add_runtime_dependency(%q, ["~> 3.1.0"]) + s.add_runtime_dependency(%q, ["~> 3.1.0"]) + s.add_runtime_dependency(%q, ["~> 3.1.0"]) else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 2.0.2"]) + s.add_dependency(%q, ["~> 3.1.0"]) + s.add_dependency(%q, ["~> 3.1.0"]) + s.add_dependency(%q, ["~> 3.1.0"]) end else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 2.0.2"]) + s.add_dependency(%q, ["~> 3.1.0"]) + s.add_dependency(%q, ["~> 3.1.0"]) + s.add_dependency(%q, ["~> 3.1.0"]) end end From 7bf339d4b8a8f3ea8957f60ab5c722f9f79da755 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Fri, 6 May 2011 08:09:01 -0400 Subject: [PATCH 05/17] Update gemspec to play nicely with 3.1.0.beta --- Rakefile | 6 +++--- meta_search.gemspec | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Rakefile b/Rakefile index 8a8be87..18a605a 100644 --- a/Rakefile +++ b/Rakefile @@ -15,9 +15,9 @@ begin gem.homepage = "http://metautonomo.us/projects/metasearch/" gem.authors = ["Ernie Miller"] gem.add_development_dependency "shoulda" - gem.add_dependency "activerecord", "~> 3.1.0" - gem.add_dependency "activesupport", "~> 3.1.0" - gem.add_dependency "actionpack", "~> 3.1.0" + gem.add_dependency "activerecord", "~> 3.1.0.alpha" + gem.add_dependency "activesupport", "~> 3.1.0.alpha" + gem.add_dependency "actionpack", "~> 3.1.0.alpha" gem.post_install_message = < 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = [%q{Ernie Miller}] - s.date = %q{2011-05-05} + s.date = %q{2011-05-06} s.description = %q{ Allows simple search forms to be created against an AR3 model and its associations, has useful view helpers for sort links @@ -79,20 +79,20 @@ you're feeling especially appreciative. It'd help me justify this if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, ["~> 3.1.0"]) - s.add_runtime_dependency(%q, ["~> 3.1.0"]) - s.add_runtime_dependency(%q, ["~> 3.1.0"]) + s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.1.0"]) - s.add_dependency(%q, ["~> 3.1.0"]) - s.add_dependency(%q, ["~> 3.1.0"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) end else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.1.0"]) - s.add_dependency(%q, ["~> 3.1.0"]) - s.add_dependency(%q, ["~> 3.1.0"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) end end From a453510cd3f692d25c96aeecc8b2a55c7cc14e32 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 26 May 2011 18:57:46 -0400 Subject: [PATCH 06/17] Update Fixtures to ActiveRecord::Fixtures --- test/helper.rb | 2 +- vendor/arel | 2 +- vendor/rails | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/helper.rb b/test/helper.rb index f53385f..009186f 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -23,7 +23,7 @@ load File.join(FIXTURES_PATH, 'schema.rb') end -Fixtures.create_fixtures(FIXTURES_PATH, ActiveRecord::Base.connection.tables) +ActiveRecord::Fixtures.create_fixtures(FIXTURES_PATH, ActiveRecord::Base.connection.tables) I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'locales', '*.yml')] diff --git a/vendor/arel b/vendor/arel index a3d9c39..58c3d1d 160000 --- a/vendor/arel +++ b/vendor/arel @@ -1 +1 @@ -Subproject commit a3d9c393b4707a03734dcf6831fb9636fabac045 +Subproject commit 58c3d1d56cf38aebf09e64803441b606680b680a diff --git a/vendor/rails b/vendor/rails index ee898e5..602fff1 160000 --- a/vendor/rails +++ b/vendor/rails @@ -1 +1 @@ -Subproject commit ee898e5248d3876372c8303f500afe2baf6f298c +Subproject commit 602fff1f58dff5cfdf861b48626b32f99fe27246 From 79806e6a9db5dc4eedbd45bccc5929cf6fbf3593 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 26 May 2011 19:02:40 -0400 Subject: [PATCH 07/17] Regenerate gemspec for version 1.1.0.pre --- meta_search.gemspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/meta_search.gemspec b/meta_search.gemspec index 19e2ec1..b949d69 100644 --- a/meta_search.gemspec +++ b/meta_search.gemspec @@ -8,8 +8,8 @@ Gem::Specification.new do |s| s.version = "1.1.0.pre" s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= - s.authors = [%q{Ernie Miller}] - s.date = %q{2011-05-06} + s.authors = ["Ernie Miller"] + s.date = %q{2011-05-26} s.description = %q{ Allows simple search forms to be created against an AR3 model and its associations, has useful view helpers for sort links @@ -70,8 +70,8 @@ you're feeling especially appreciative. It'd help me justify this "open source" stuff to my lovely wife. :) } - s.require_paths = [%q{lib}] - s.rubygems_version = %q{1.8.0} + s.require_paths = ["lib"] + s.rubygems_version = %q{1.7.2} s.summary = %q{Object-based searching (and more) for simply creating search forms.} if s.respond_to? :specification_version then From 9ed13d3c67365a3b4450badc90394e9e17635493 Mon Sep 17 00:00:00 2001 From: Coby Chapple Date: Fri, 1 Jul 2011 23:10:38 +1000 Subject: [PATCH 08/17] Changed parameter name to :default_order, and moved beneath the end of the form_for explanation section --- README.rdoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rdoc b/README.rdoc index 73d37ac..da7a340 100644 --- a/README.rdoc +++ b/README.rdoc @@ -314,15 +314,15 @@ Or, if in the context of a form_for against a MetaSearch::Builder: <%= f.sort_link :title %> -By default, the link that is created will sort by the given column in ascending order when first clicked. If you'd like to reverse this (so the first click sorts the results in descending order), you can pass `:sort_preference=>:desc` in the options hash, like so: - - <%= sort_link @search, :ratings, "Highest Rated", :sort_preference=>:desc %> - The @search object is the instance of MetaSearch::Builder you got back earlier from your controller. The other required parameter is the attribute name itself. Optionally, you can provide a string as a 3rd parameter to override the default link name, and then additional hashed for the +options+ and +html_options+ hashes for link_to. +By default, the link that is created will sort by the given column in ascending order when first clicked. If you'd like to reverse this (so the first click sorts the results in descending order), you can pass +:default_order => :desc+ in the options hash, like so: + + <%= sort_link @search, :ratings, "Highest Rated", :default_order => :desc %> + You can sort by more than one column as well, by creating a link like: <%= sort_link :name_and_salary %> From 963d2ad1ec118e77a36ab6e1e430a42ab8e42fd7 Mon Sep 17 00:00:00 2001 From: Coby Chapple Date: Fri, 1 Jul 2011 23:12:49 +1000 Subject: [PATCH 09/17] removed defined? check for the hash value as per ernie's comment --- lib/meta_search/helpers/url_helper.rb | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/meta_search/helpers/url_helper.rb b/lib/meta_search/helpers/url_helper.rb index 7084f03..82d3d46 100644 --- a/lib/meta_search/helpers/url_helper.rb +++ b/lib/meta_search/helpers/url_helper.rb @@ -16,23 +16,27 @@ module UrlHelper # <%= sort_link @search, :name, 'Company Name' %> # <%= sort_link @search, :name, :class => 'name_sort' %> # <%= sort_link @search, :name, 'Company Name', :class => 'company_name_sort' %> + # <%= sort_link @search, :name, :default_order => :desc %> + # <%= sort_link @search, :name, 'Company Name', :default_order => :desc %> + # <%= sort_link @search, :name, :class => 'name_sort', :default_order => :desc %> + # <%= sort_link @search, :name, 'Company Name', :class => 'company_name_sort', :default_order => :desc %> + def sort_link(builder, attribute, *args) raise ArgumentError, "Need a MetaSearch::Builder search object as first param!" unless builder.is_a?(MetaSearch::Builder) attr_name = attribute.to_s name = (args.size > 0 && !args.first.is_a?(Hash)) ? args.shift.to_s : builder.base.human_attribute_name(attr_name) prev_attr, prev_order = builder.search_attributes['meta_sort'].to_s.split('.') - + options = args.first.is_a?(Hash) ? args.shift : {} current_order = prev_attr == attr_name ? prev_order : nil - - if defined?(options[:sort_preference]) && options[:sort_preference]==:desc + + if options[:default_order] == :desc new_order = current_order == 'desc' ? 'asc' : 'desc' - options.delete(:sort_preference) else new_order = current_order == 'asc' ? 'desc' : 'asc' - options.delete(:sort_preference) - end - + end + options.delete(:default_order) + html_options = args.first.is_a?(Hash) ? args.shift : {} css = ['sort_link', current_order].compact.join(' ') html_options[:class] = [css, html_options[:class]].compact.join(' ') @@ -59,4 +63,4 @@ def order_indicator_for(order) end end end -end \ No newline at end of file +end From 9f085c22fd02835373a166274fd3e2933428cd63 Mon Sep 17 00:00:00 2001 From: Coby Chapple Date: Fri, 1 Jul 2011 23:13:07 +1000 Subject: [PATCH 10/17] added some basic tests for the :default_order parameter --- test/test_view_helpers.rb | 53 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/test/test_view_helpers.rb b/test/test_view_helpers.rb index 8596f71..b4518d8 100644 --- a/test/test_view_helpers.rb +++ b/test/test_view_helpers.rb @@ -305,6 +305,21 @@ def setup @s = Company.search end + should "generate a sort link for descending order if set as the default order" do + assert_match /name.desc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :desc) + end + + should "generate a sort link for ascending order if set as the default order" do + assert_match /name.asc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :asc) + end + + should "generate a sort link for ascending order if default is specified incorectly" do + assert_match /name.asc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :something_else) + end + context "sorted by name ascending" do setup do @s.meta_sort = 'name.asc' @@ -320,6 +335,21 @@ def setup sort_link(@s, :created_at, :controller => 'companies') end + should "generate a sort link for descending order if ascending order is the default" do + assert_match /name.desc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :asc) + end + + should "generate a sort link for descending order if descending order is the default" do + assert_match /name.desc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :desc) + end + + should "generate a sort link for descending order if default is specified incorrectly" do + assert_match /name.desc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :something_else) + end + context "with existing search options" do setup do @s.name_contains = 'a' @@ -331,6 +361,27 @@ def setup end end end + + context "sorted by name descending" do + setup do + @s.meta_sort = 'name.desc' + end + + should "generate a sort link for ascending order if descending order is the default" do + assert_match /name.asc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :desc) + end + + should "generate a sort link for ascending order if ascending order is the default" do + assert_match /name.asc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :asc) + end + + should "generate a sort link for ascending order if default is specified incorrectly" do + assert_match /name.asc/, + sort_link(@s, :name, :controller => 'companies', :default_order => :desc) + end + end end context "A developer search" do @@ -365,4 +416,4 @@ def setup end end end -end \ No newline at end of file +end From 85f6bec52dffc3174f9e99d3f40255bfffd85913 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Mon, 4 Jul 2011 15:00:37 -0400 Subject: [PATCH 11/17] Fix time zone stuff in tests --- test/fixtures/data_types.yml | 6 +++--- test/helper.rb | 1 + vendor/arel | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/fixtures/data_types.yml b/test/fixtures/data_types.yml index 1b8d6e1..e8f81f3 100644 --- a/test/fixtures/data_types.yml +++ b/test/fixtures/data_types.yml @@ -6,9 +6,9 @@ dt_<%= n %>: int : <%= n ** 3 %> flt : <%= n.to_f / 2.0 %> dec : <%= n.to_f ** (n + 0.1) %> - dtm : <%= (Time.utc(2009, 12, 24) + 86400 * n).to_s(:db) %> - tms : <%= (Time.utc(2009, 12, 24) + 86400 * n).to_s(:db) %> - tim : <%= Time.utc(2000, 01, 01, n+8, n).to_s(:db) %> + dtm : <%= (Time.local(2009, 12, 24) + 86400 * n).in_time_zone.to_s(:db) %> + tms : <%= (Time.local(2009, 12, 24) + 86400 * n).in_time_zone.to_s(:db) %> + tim : <%= Time.local(2000, 01, 01, n+8, n).in_time_zone.to_s(:db) %> dat : <%= (Date.new(2009, 12, 24) + n).strftime("%Y-%m-%d") %> bin : <%= "BLOB#{n}" * n %> bln : <%= n % 2 > 0 ? true : false %> diff --git a/test/helper.rb b/test/helper.rb index 009186f..4c42f10 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -1,6 +1,7 @@ require 'rubygems' require 'test/unit' require 'shoulda' +require 'active_support/time' require 'active_record' require 'active_record/fixtures' require 'action_view' diff --git a/vendor/arel b/vendor/arel index 58c3d1d..7832cd3 160000 --- a/vendor/arel +++ b/vendor/arel @@ -1 +1 @@ -Subproject commit 58c3d1d56cf38aebf09e64803441b606680b680a +Subproject commit 7832cd3bb3c6cfd76fdb63ca75995f2e6c87757c From 03c632912435217837a620db29acf18290ce9848 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 7 Jul 2011 15:25:48 -0400 Subject: [PATCH 12/17] Fix does_not_start_with --- VERSION | 2 +- lib/meta_search.rb | 2 +- vendor/arel | 2 +- vendor/rails | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index d1ced49..63f2e2a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0.pre \ No newline at end of file +1.1.0.pre2 \ No newline at end of file diff --git a/lib/meta_search.rb b/lib/meta_search.rb index 7dbeb45..41d8d84 100644 --- a/lib/meta_search.rb +++ b/lib/meta_search.rb @@ -15,7 +15,7 @@ module MetaSearch ['contains', 'like', 'matches', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}%"'}], ['does_not_contain', 'nlike', 'not_matches', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"%#{param}%"'}], ['starts_with', 'sw', {:types => STRINGS, :predicate => :matches, :formatter => '"#{param}%"'}], - ['does_not_start_with', 'dnsw', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"%#{param}%"'}], + ['does_not_start_with', 'dnsw', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"#{param}%"'}], ['ends_with', 'ew', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}"'}], ['does_not_end_with', 'dnew', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"%#{param}"'}], ['greater_than', 'gt', {:types => (NUMBERS + DATES + TIMES), :predicate => :gt}], diff --git a/vendor/arel b/vendor/arel index 7832cd3..0e6f943 160000 --- a/vendor/arel +++ b/vendor/arel @@ -1 +1 @@ -Subproject commit 7832cd3bb3c6cfd76fdb63ca75995f2e6c87757c +Subproject commit 0e6f943096b38bdac8eb5a8b468579ba7883772a diff --git a/vendor/rails b/vendor/rails index 602fff1..072e1ca 160000 --- a/vendor/rails +++ b/vendor/rails @@ -1 +1 @@ -Subproject commit 602fff1f58dff5cfdf861b48626b32f99fe27246 +Subproject commit 072e1ca5ea70d746189be360c64270b13c745bc3 From adc872a61fa94869b72f29901c93482f3181b68e Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 7 Jul 2011 15:32:36 -0400 Subject: [PATCH 13/17] Regenerate gemspec for version 1.0.6 --- meta_search.gemspec | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/meta_search.gemspec b/meta_search.gemspec index b949d69..5639905 100644 --- a/meta_search.gemspec +++ b/meta_search.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = %q{meta_search} - s.version = "1.1.0.pre" + s.version = "1.0.6" - s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= - s.authors = ["Ernie Miller"] - s.date = %q{2011-05-26} + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = [%q{Ernie Miller}] + s.date = %q{2011-07-07} s.description = %q{ Allows simple search forms to be created against an AR3 model and its associations, has useful view helpers for sort links @@ -70,8 +70,8 @@ you're feeling especially appreciative. It'd help me justify this "open source" stuff to my lovely wife. :) } - s.require_paths = ["lib"] - s.rubygems_version = %q{1.7.2} + s.require_paths = [%q{lib}] + s.rubygems_version = %q{1.8.5} s.summary = %q{Object-based searching (and more) for simply creating search forms.} if s.respond_to? :specification_version then @@ -79,20 +79,23 @@ you're feeling especially appreciative. It'd help me justify this if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) - s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) - s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_runtime_dependency(%q, ["~> 3.0.2"]) + s.add_runtime_dependency(%q, ["~> 3.0.2"]) + s.add_runtime_dependency(%q, ["~> 3.0.2"]) + s.add_runtime_dependency(%q, ["~> 2.0.2"]) else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.1.0.alpha"]) - s.add_dependency(%q, ["~> 3.1.0.alpha"]) - s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.0.2"]) + s.add_dependency(%q, ["~> 3.0.2"]) + s.add_dependency(%q, ["~> 3.0.2"]) + s.add_dependency(%q, ["~> 2.0.2"]) end else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.1.0.alpha"]) - s.add_dependency(%q, ["~> 3.1.0.alpha"]) - s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.0.2"]) + s.add_dependency(%q, ["~> 3.0.2"]) + s.add_dependency(%q, ["~> 3.0.2"]) + s.add_dependency(%q, ["~> 2.0.2"]) end end From bec24373be4b39e086a053dd5fc87c07e0385541 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 7 Jul 2011 15:33:00 -0400 Subject: [PATCH 14/17] Curse you, submodules. --- vendor/arel | 2 +- vendor/rails | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/arel b/vendor/arel index 0e6f943..a6d5f82 160000 --- a/vendor/arel +++ b/vendor/arel @@ -1 +1 @@ -Subproject commit 0e6f943096b38bdac8eb5a8b468579ba7883772a +Subproject commit a6d5f823a5d74c6884adfc88c30ad05624c4d674 diff --git a/vendor/rails b/vendor/rails index 072e1ca..e9f2c67 160000 --- a/vendor/rails +++ b/vendor/rails @@ -1 +1 @@ -Subproject commit 072e1ca5ea70d746189be360c64270b13c745bc3 +Subproject commit e9f2c67874a8b9a2f1fd4df068f8640bb266f139 From dfdd9c3a9956c4a2ad96724e2a801ad77a962b70 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 7 Jul 2011 15:33:25 -0400 Subject: [PATCH 15/17] Regenerate gemspec for version 1.1.0.pre2 --- meta_search.gemspec | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/meta_search.gemspec b/meta_search.gemspec index 5639905..4160593 100644 --- a/meta_search.gemspec +++ b/meta_search.gemspec @@ -5,9 +5,9 @@ Gem::Specification.new do |s| s.name = %q{meta_search} - s.version = "1.0.6" + s.version = "1.1.0.pre2" - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = [%q{Ernie Miller}] s.date = %q{2011-07-07} s.description = %q{ @@ -79,23 +79,20 @@ you're feeling especially appreciative. It'd help me justify this if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, ["~> 3.0.2"]) - s.add_runtime_dependency(%q, ["~> 3.0.2"]) - s.add_runtime_dependency(%q, ["~> 3.0.2"]) - s.add_runtime_dependency(%q, ["~> 2.0.2"]) + s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_runtime_dependency(%q, ["~> 3.1.0.alpha"]) else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 2.0.2"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) end else s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 3.0.2"]) - s.add_dependency(%q, ["~> 2.0.2"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) + s.add_dependency(%q, ["~> 3.1.0.alpha"]) end end From b31bf6d779bab19ee52e64b78f8818e72da6dd5f Mon Sep 17 00:00:00 2001 From: Dessy Daskalov Date: Thu, 11 Aug 2011 14:57:13 -0400 Subject: [PATCH 16/17] Sort link should not modify passed-in parameters --- lib/meta_search/helpers/url_helper.rb | 2 +- test/test_view_helpers.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/meta_search/helpers/url_helper.rb b/lib/meta_search/helpers/url_helper.rb index 82d3d46..24be8e9 100644 --- a/lib/meta_search/helpers/url_helper.rb +++ b/lib/meta_search/helpers/url_helper.rb @@ -27,7 +27,7 @@ def sort_link(builder, attribute, *args) name = (args.size > 0 && !args.first.is_a?(Hash)) ? args.shift.to_s : builder.base.human_attribute_name(attr_name) prev_attr, prev_order = builder.search_attributes['meta_sort'].to_s.split('.') - options = args.first.is_a?(Hash) ? args.shift : {} + options = args.first.is_a?(Hash) ? args.shift.clone : {} current_order = prev_attr == attr_name ? prev_order : nil if options[:default_order] == :desc diff --git a/test/test_view_helpers.rb b/test/test_view_helpers.rb index c94bdbd..b9a1d72 100644 --- a/test/test_view_helpers.rb +++ b/test/test_view_helpers.rb @@ -432,4 +432,16 @@ def setup end end end + + context "Any search" do + setup do + @s = Company.search(:name_contains => 'foo') + end + + should "not modify passed-in parameters" do + params = { :controller => 'companies' } + sort_link(@s, :name, params) + assert_equal ({ :controller => 'companies' }), params + end + end end From ab42efc520236aef2d6df9c0c5855ab73319bb30 Mon Sep 17 00:00:00 2001 From: Dessy Daskalov Date: Mon, 28 Nov 2011 16:33:49 -0500 Subject: [PATCH 17/17] Replace clone with dup --- lib/meta_search/helpers/url_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/meta_search/helpers/url_helper.rb b/lib/meta_search/helpers/url_helper.rb index 24be8e9..1b3c818 100644 --- a/lib/meta_search/helpers/url_helper.rb +++ b/lib/meta_search/helpers/url_helper.rb @@ -27,7 +27,7 @@ def sort_link(builder, attribute, *args) name = (args.size > 0 && !args.first.is_a?(Hash)) ? args.shift.to_s : builder.base.human_attribute_name(attr_name) prev_attr, prev_order = builder.search_attributes['meta_sort'].to_s.split('.') - options = args.first.is_a?(Hash) ? args.shift.clone : {} + options = args.first.is_a?(Hash) ? args.shift.dup : {} current_order = prev_attr == attr_name ? prev_order : nil if options[:default_order] == :desc