From ea5eff64a27acd7d1e9a7647b55c364dd7e2779b Mon Sep 17 00:00:00 2001 From: Nicolae Claudius Date: Thu, 12 Jan 2012 11:19:26 -0800 Subject: [PATCH] domain#name_reversed cache for indexing and searching and domain ownership validation accounting for permissions --- app/models/domain.rb | 6 +++++- app/models/user.rb | 6 ++++++ ...0112175527_add_name_reversed_to_domains.rb | 15 ++++++++++++++ spec/models/domain_spec.rb | 20 ++++++++++++++++--- spec/support/shared_context/data.rb | 3 +++ 5 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20120112175527_add_name_reversed_to_domains.rb diff --git a/app/models/domain.rb b/app/models/domain.rb index 07286bf..670fc19 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -50,7 +50,7 @@ class Domain < ActiveRecord::Base errors[:name] = "cannot be a TLD or a reserved domain" if Tld.include?(name) # if parent domain is on our system, the user must own it - if parent_domain.present? && parent_domain.user_id != user_id + if parent_domain.present? && user.cannot?(:manage, parent_domain) errors[:name] = "issue, the parent domain `#{parent_domain.name}` is registered to another user" end end @@ -74,6 +74,10 @@ class Domain < ActiveRecord::Base a_records.build(:content => ip) if ip.present? end + before_save do + self.name_reversed = name.reverse if name_changed? + end + before_validation(:on => :update) do if name_changed? name_was_pattern = /#{Regexp.escape(name_was)}$/ diff --git a/app/models/user.rb b/app/models/user.rb index 3c64845..c04130e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -24,4 +24,10 @@ class User < ActiveRecord::Base "#{first_name} #{last_name}" end + delegate :can?, :cannot?, :to => :ability + + def ability + @ability ||= Ability.new(self) + end + end diff --git a/db/migrate/20120112175527_add_name_reversed_to_domains.rb b/db/migrate/20120112175527_add_name_reversed_to_domains.rb new file mode 100644 index 0000000..a67257c --- /dev/null +++ b/db/migrate/20120112175527_add_name_reversed_to_domains.rb @@ -0,0 +1,15 @@ +class AddNameReversedToDomains < ActiveRecord::Migration + def up + # used for "%" search indexing in an ancestry fashion (materialized path pattern) + # http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html + add_column :domains, :name_reversed, :string, :limit => 255 + execute "UPDATE domains SET name_reversed = REVERSE(name)" + change_column :domains, :name_reversed, :string, :limit => 255, :null => false + add_index :domains, :name_reversed + end + + def down + remove_index :domains, :column => :name_reversed + remove_column :domains, :name_reversed + end +end diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb index ef30762..d7788c5 100644 --- a/spec/models/domain_spec.rb +++ b/spec/models/domain_spec.rb @@ -46,6 +46,11 @@ describe Domain do domain.should be_valid end + it "has parent_domain" do + subdomain = build(:domain, :user => other_user, :name => "x.#{domain.name}") + subdomain.parent_domain.should == domain + end + it "validates ownership" do domain.name = 'co.uk' domain.should have(1).errors_on(:name) @@ -53,9 +58,13 @@ describe Domain do domain.name = 'clyfe.ro' domain.should be_valid - # stub a parent domain on another user account - mock_domain = mock(:user_id => domain.user_id + 1, :name => 'x') - Domain.stub_chain('find_by_name').and_return(mock_domain) + # stub a parent domain on another user account, with no permissions present + mock_domain = mock( + :user_id => third_user.id, + :user => third_user, + :name => 'x' + ) + Domain.stub(:find_by_name).and_return(mock_domain) domain.should have(1).errors_on(:name) end @@ -66,4 +75,9 @@ describe Domain do joins.should == [[:permissions, Arel::Nodes::OuterJoin]] end + it "has reversed name" do + domain.name_reversed.should be_present + domain.name_reversed.should == domain.name.reverse + end + end diff --git a/spec/support/shared_context/data.rb b/spec/support/shared_context/data.rb index 829b5a9..faee21b 100644 --- a/spec/support/shared_context/data.rb +++ b/spec/support/shared_context/data.rb @@ -5,6 +5,9 @@ shared_context "data" do let(:other_user){create(:user)} let(:other_user_ability){Ability.new(:user => other_user)} + + let(:third_user){create(:user)} + let(:third_user_ability){Ability.new(:user => third_user)} let(:domain){ domain = build(:domain, :user => user)