method_scoping_test.rb 16.0 KB
Newer Older
1
require "cases/helper"
J
Jeremy Kemper 已提交
2 3 4 5 6
require 'models/developer'
require 'models/project'
require 'models/comment'
require 'models/post'
require 'models/category'
7

8
class MethodScopingTest < ActiveRecord::TestCase
9
  fixtures :developers, :projects, :comments, :posts
J
Jeremy Kemper 已提交
10

11
  def test_set_conditions
12
    Developer.with_scope(:find => { :conditions => 'just a test...' }) do
13
      assert_equal 'just a test...', Developer.send(:current_scoped_methods)[:find][:conditions]
14 15 16 17
    end
  end

  def test_scoped_find
18
    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
19 20 21
      assert_nothing_raised { Developer.find(1) }
    end
  end
J
Jeremy Kemper 已提交
22

23
  def test_scoped_find_first
24
    Developer.with_scope(:find => { :conditions => "salary = 100000" }) do
25 26 27
      assert_equal Developer.find(10), Developer.find(:first, :order => 'name')
    end
  end
J
Jeremy Kemper 已提交
28

M
Marcel Molina 已提交
29 30 31 32 33
  def test_scoped_find_combines_conditions
    Developer.with_scope(:find => { :conditions => "salary = 9000" }) do
      assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => "name = 'Jamis'")
    end
  end
J
Jeremy Kemper 已提交
34

M
Marcel Molina 已提交
35 36 37
  def test_scoped_find_sanitizes_conditions
    Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
      assert_equal developers(:poor_jamis), Developer.find(:first)
J
Jeremy Kemper 已提交
38
    end
M
Marcel Molina 已提交
39
  end
J
Jeremy Kemper 已提交
40

M
Marcel Molina 已提交
41 42 43
  def test_scoped_find_combines_and_sanitizes_conditions
    Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
      assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => ['name = ?', 'Jamis'])
J
Jeremy Kemper 已提交
44
    end
M
Marcel Molina 已提交
45
  end
J
Jeremy Kemper 已提交
46

47
  def test_scoped_find_all
48
    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
M
Marcel Molina 已提交
49
      assert_equal [developers(:david)], Developer.find(:all)
J
Jeremy Kemper 已提交
50
    end
51
  end
J
Jeremy Kemper 已提交
52

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
  def test_scoped_find_select
    Developer.with_scope(:find => { :select => "id, name" }) do
      developer = Developer.find(:first, :conditions => "name = 'David'")
      assert_equal "David", developer.name
      assert !developer.has_attribute?(:salary)
    end
  end

  def test_options_select_replaces_scope_select
    Developer.with_scope(:find => { :select => "id, name" }) do
      developer = Developer.find(:first, :select => 'id, salary', :conditions => "name = 'David'")
      assert_equal 80000, developer.salary
      assert !developer.has_attribute?(:name)
    end
  end

69
  def test_scoped_count
70
    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
71
      assert_equal 1, Developer.count
J
Jeremy Kemper 已提交
72
    end
73

74
    Developer.with_scope(:find => { :conditions => 'salary = 100000' }) do
75
      assert_equal 8, Developer.count
76
      assert_equal 1, Developer.count(:conditions => "name LIKE 'fixture_1%'")
J
Jeremy Kemper 已提交
77
    end
78
  end
79

80 81 82 83 84 85 86 87 88
  def test_scoped_find_include
    # with the include, will retrieve only developers for the given project
    scoped_developers = Developer.with_scope(:find => { :include => :projects }) do
      Developer.find(:all, :conditions => 'projects.id = 2')
    end
    assert scoped_developers.include?(developers(:david))
    assert !scoped_developers.include?(developers(:jamis))
    assert_equal 1, scoped_developers.size
  end
J
Jeremy Kemper 已提交
89

90 91 92 93 94 95 96 97 98 99
  def test_scoped_find_joins
    scoped_developers = Developer.with_scope(:find => { :joins => 'JOIN developers_projects ON id = developer_id' } ) do
      Developer.find(:all, :conditions => 'developers_projects.project_id = 2')
    end
    assert scoped_developers.include?(developers(:david))
    assert !scoped_developers.include?(developers(:jamis))
    assert_equal 1, scoped_developers.size
    assert_equal developers(:david).attributes, scoped_developers.first.attributes
  end

100 101 102
  def test_scoped_count_include
    # with the include, will retrieve only developers for the given project
    Developer.with_scope(:find => { :include => :projects }) do
103
      assert_equal 1, Developer.count(:conditions => 'projects.id = 2')
104 105 106
    end
  end

107 108 109
  def test_scoped_create
    new_comment = nil

110
    VerySpecialComment.with_scope(:create => { :post_id => 1 }) do
111
      assert_equal({ :post_id => 1 }, VerySpecialComment.send(:current_scoped_methods)[:create])
112 113
      new_comment = VerySpecialComment.create :body => "Wonderful world"
    end
114

115 116 117
    assert Post.find(1).comments.include?(new_comment)
  end

118
  def test_immutable_scope
119
    options = { :conditions => "name = 'David'" }
120
    Developer.with_scope(:find => options) do
121 122 123 124
      assert_equal %w(David), Developer.find(:all).map { |d| d.name }
      options[:conditions] = "name != 'David'"
      assert_equal %w(David), Developer.find(:all).map { |d| d.name }
    end
125 126 127 128 129 130 131 132 133

    scope = { :find => { :conditions => "name = 'David'" }}
    Developer.with_scope(scope) do
      assert_equal %w(David), Developer.find(:all).map { |d| d.name }
      scope[:find][:conditions] = "name != 'David'"
      assert_equal %w(David), Developer.find(:all).map { |d| d.name }
    end
  end

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
  def test_scoped_with_duck_typing
    scoping = Struct.new(:method_scoping).new(:find => { :conditions => ["name = ?", 'David'] })
    Developer.with_scope(scoping) do
       assert_equal %w(David), Developer.find(:all).map { |d| d.name }
    end
  end

  def test_ensure_that_method_scoping_is_correctly_restored
    scoped_methods = Developer.instance_eval('current_scoped_methods')

    begin
      Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
        raise "an exception"
      end
    rescue
    end
    assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
  end
end

154
class NestedScopingTest < ActiveRecord::TestCase
155
  fixtures :developers, :projects, :comments, :posts
156 157 158 159 160 161 162 163 164 165 166 167 168 169

  def test_merge_options
    Developer.with_scope(:find => { :conditions => 'salary = 80000' }) do
      Developer.with_scope(:find => { :limit => 10 }) do
        merged_option = Developer.instance_eval('current_scoped_methods')[:find]
        assert_equal({ :conditions => 'salary = 80000', :limit => 10 }, merged_option)
      end
    end
  end

  def test_replace_options
    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
      Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
        assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Developer.instance_eval('current_scoped_methods'))
170
        assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Developer.send(:scoped_methods)[-1])
171 172 173 174 175 176 177 178
      end
    end
  end

  def test_append_conditions
    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
      Developer.with_scope(:find => { :conditions => 'salary = 80000' }) do
        appended_condition = Developer.instance_eval('current_scoped_methods')[:find][:conditions]
179
        assert_equal("(name = 'David') AND (salary = 80000)", appended_condition)
180 181 182 183 184 185 186 187 188 189 190 191
        assert_equal(1, Developer.count)
      end
      Developer.with_scope(:find => { :conditions => "name = 'Maiha'" }) do
        assert_equal(0, Developer.count)
      end
    end
  end

  def test_merge_and_append_options
    Developer.with_scope(:find => { :conditions => 'salary = 80000', :limit => 10 }) do
      Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
        merged_option = Developer.instance_eval('current_scoped_methods')[:find]
192
        assert_equal({ :conditions => "(salary = 80000) AND (name = 'David')", :limit => 10 }, merged_option)
193 194 195 196 197 198 199 200 201 202 203 204 205 206
      end
    end
  end

  def test_nested_scoped_find
    Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
      Developer.with_exclusive_scope(:find => { :conditions => "name = 'David'" }) do
        assert_nothing_raised { Developer.find(1) }
        assert_equal('David', Developer.find(:first).name)
      end
      assert_equal('Jamis', Developer.find(:first).name)
    end
  end

207 208 209 210 211 212
  def test_nested_scoped_find_include
    Developer.with_scope(:find => { :include => :projects }) do
      Developer.with_scope(:find => { :conditions => "projects.id = 2" }) do
        assert_nothing_raised { Developer.find(1) }
        assert_equal('David', Developer.find(:first).name)
      end
J
Jeremy Kemper 已提交
213
    end
214 215 216 217 218 219 220 221 222
  end

  def test_nested_scoped_find_merged_include
    # :include's remain unique and don't "double up" when merging
    Developer.with_scope(:find => { :include => :projects, :conditions => "projects.id = 2" }) do
      Developer.with_scope(:find => { :include => :projects }) do
        assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
        assert_equal('David', Developer.find(:first).name)
      end
J
Jeremy Kemper 已提交
223 224
    end

225 226 227 228 229 230
    # the nested scope doesn't remove the first :include
    Developer.with_scope(:find => { :include => :projects, :conditions => "projects.id = 2" }) do
      Developer.with_scope(:find => { :include => [] }) do
        assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
        assert_equal('David', Developer.find(:first).name)
      end
J
Jeremy Kemper 已提交
231 232
    end

233 234 235 236 237 238
    # mixing array and symbol include's will merge correctly
    Developer.with_scope(:find => { :include => [:projects], :conditions => "projects.id = 2" }) do
      Developer.with_scope(:find => { :include => :projects }) do
        assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
        assert_equal('David', Developer.find(:first).name)
      end
J
Jeremy Kemper 已提交
239
    end
240
  end
J
Jeremy Kemper 已提交
241

242 243 244 245 246
  def test_nested_scoped_find_replace_include
    Developer.with_scope(:find => { :include => :projects }) do
      Developer.with_exclusive_scope(:find => { :include => [] }) do
        assert_equal 0, Developer.instance_eval('current_scoped_methods')[:find][:include].length
      end
J
Jeremy Kemper 已提交
247
    end
248 249
  end

250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
  def test_three_level_nested_exclusive_scoped_find
    Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
      assert_equal('Jamis', Developer.find(:first).name)

      Developer.with_exclusive_scope(:find => { :conditions => "name = 'David'" }) do
        assert_equal('David', Developer.find(:first).name)

        Developer.with_exclusive_scope(:find => { :conditions => "name = 'Maiha'" }) do
          assert_equal(nil, Developer.find(:first))
        end

        # ensure that scoping is restored
        assert_equal('David', Developer.find(:first).name)
      end

      # ensure that scoping is restored
      assert_equal('Jamis', Developer.find(:first).name)
    end
  end

  def test_merged_scoped_find
    poor_jamis = developers(:poor_jamis)
    Developer.with_scope(:find => { :conditions => "salary < 100000" }) do
      Developer.with_scope(:find => { :offset => 1 }) do
274
        assert_equal(poor_jamis, Developer.find(:first, :order => 'id asc'))
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
      end
    end
  end

  def test_merged_scoped_find_sanitizes_conditions
    Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
      Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
        assert_raise(ActiveRecord::RecordNotFound) { developers(:poor_jamis) }
      end
    end
  end

  def test_nested_scoped_find_combines_and_sanitizes_conditions
    Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
      Developer.with_exclusive_scope(:find => { :conditions => ['salary = ?', 9000] }) do
        assert_equal developers(:poor_jamis), Developer.find(:first)
        assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => ['name = ?', 'Jamis'])
      end
    end
  end

  def test_merged_scoped_find_combines_and_sanitizes_conditions
    Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
      Developer.with_scope(:find => { :conditions => ['salary > ?', 9000] }) do
        assert_equal %w(David), Developer.find(:all).map { |d| d.name }
      end
    end
  end

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
  def test_merged_scoped_find_on_blank_conditions
    [nil, " ", [], {}].each do |blank|
      Developer.with_scope(:find => {:conditions => blank}) do
        Developer.with_scope(:find => {:conditions => blank}) do
          assert_nothing_raised { Developer.find(:first) }
        end
      end
    end
  end

  def test_merged_scoped_find_on_blank_bind_conditions
    [ [""], ["",{}] ].each do |blank|
      Developer.with_scope(:find => {:conditions => blank}) do
        Developer.with_scope(:find => {:conditions => blank}) do
          assert_nothing_raised { Developer.find(:first) }
        end
      end
    end
  end

324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
  def test_immutable_nested_scope
    options1 = { :conditions => "name = 'Jamis'" }
    options2 = { :conditions => "name = 'David'" }
    Developer.with_scope(:find => options1) do
      Developer.with_exclusive_scope(:find => options2) do
        assert_equal %w(David), Developer.find(:all).map { |d| d.name }
        options1[:conditions] = options2[:conditions] = nil
        assert_equal %w(David), Developer.find(:all).map { |d| d.name }
      end
    end
  end

  def test_immutable_merged_scope
    options1 = { :conditions => "name = 'Jamis'" }
    options2 = { :conditions => "salary > 10000" }
    Developer.with_scope(:find => options1) do
      Developer.with_scope(:find => options2) do
        assert_equal %w(Jamis), Developer.find(:all).map { |d| d.name }
        options1[:conditions] = options2[:conditions] = nil
        assert_equal %w(Jamis), Developer.find(:all).map { |d| d.name }
      end
    end
  end

  def test_ensure_that_method_scoping_is_correctly_restored
    Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
      scoped_methods = Developer.instance_eval('current_scoped_methods')
      begin
        Developer.with_scope(:find => { :conditions => "name = 'Maiha'" }) do
          raise "an exception"
        end
      rescue
356
      end
357
      assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
358
    end
359
  end
360 361
end

362
class HasManyScopingTest< ActiveRecord::TestCase
363
  fixtures :comments, :posts
J
Jeremy Kemper 已提交
364

365 366 367
  def setup
    @welcome = Post.find(1)
  end
J
Jeremy Kemper 已提交
368

369 370 371 372 373 374 375 376 377
  def test_forwarding_of_static_methods
    assert_equal 'a comment...', Comment.what_are_you
    assert_equal 'a comment...', @welcome.comments.what_are_you
  end

  def test_forwarding_to_scoped
    assert_equal 4, Comment.search_by_type('Comment').size
    assert_equal 2, @welcome.comments.search_by_type('Comment').size
  end
J
Jeremy Kemper 已提交
378

379 380 381 382
  def test_forwarding_to_dynamic_finders
    assert_equal 4, Comment.find_all_by_type('Comment').size
    assert_equal 2, @welcome.comments.find_all_by_type('Comment').size
  end
383

384
  def test_nested_scope
385
    Comment.with_scope(:find => { :conditions => '1=1' }) do
386
      assert_equal 'a comment...', @welcome.comments.what_are_you
387 388
    end
  end
389 390 391
end


392
class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
393
  fixtures :posts, :categories, :categories_posts
394 395 396 397 398 399 400 401 402 403 404

  def setup
    @welcome = Post.find(1)
  end

  def test_forwarding_of_static_methods
    assert_equal 'a category...', Category.what_are_you
    assert_equal 'a category...', @welcome.categories.what_are_you
  end

  def test_forwarding_to_dynamic_finders
J
Jeremy Kemper 已提交
405
    assert_equal 4, Category.find_all_by_type('SpecialCategory').size
406 407 408
    assert_equal 0, @welcome.categories.find_all_by_type('SpecialCategory').size
    assert_equal 2, @welcome.categories.find_all_by_type('Category').size
  end
409

410
  def test_nested_scope
411
    Category.with_scope(:find => { :conditions => '1=1' }) do
412
      assert_equal 'a comment...', @welcome.comments.what_are_you
413 414
    end
  end
415 416 417 418 419 420 421
end


=begin
# We disabled the scoping for has_one and belongs_to as we can't think of a proper use case


422
class BelongsToScopingTest< ActiveRecord::TestCase
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
  fixtures :comments, :posts

  def setup
    @greetings = Comment.find(1)
  end

  def test_forwarding_of_static_method
    assert_equal 'a post...', Post.what_are_you
    assert_equal 'a post...', @greetings.post.what_are_you
  end

  def test_forwarding_to_dynamic_finders
    assert_equal 4, Post.find_all_by_type('Post').size
    assert_equal 1, @greetings.post.find_all_by_type('Post').size
  end

end


442
class HasOneScopingTest< ActiveRecord::TestCase
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
  fixtures :comments, :posts

  def setup
    @sti_comments = Post.find(4)
  end

  def test_forwarding_of_static_methods
    assert_equal 'a comment...', Comment.what_are_you
    assert_equal 'a very special comment...', @sti_comments.very_special_comment.what_are_you
  end

  def test_forwarding_to_dynamic_finders
    assert_equal 1, Comment.find_all_by_type('VerySpecialComment').size
    assert_equal 1, @sti_comments.very_special_comment.find_all_by_type('VerySpecialComment').size
    assert_equal 0, @sti_comments.very_special_comment.find_all_by_type('Comment').size
  end

end

462
=end