Commit ebbb202a280f1f163a6be2b117492053a6769d7b

Authored by Joenio Costa
Committed by Daniela Feitosa
1 parent 97633c05

Using a ruby way to randomize people from ProfileListBlock

- Added 2 named scopes to select things with and without images
- Priorize profiles with image by defaut in ProfileListBlock
- Migration to remove unused 'image' column from products
- Migration to move image references to owner of image

(ActionItem1971)
app/models/image.rb
1 class Image < ActiveRecord::Base 1 class Image < ActiveRecord::Base
2 - belongs_to :owner, :polymorphic => true  
3 2
4 def self.max_size 3 def self.max_size
5 Image.attachment_options[:max_size] 4 Image.attachment_options[:max_size]
app/models/profile_list_block.rb
1 class ProfileListBlock < Block 1 class ProfileListBlock < Block
2 2
3 settings_items :limit, :type => :integer, :default => 6 3 settings_items :limit, :type => :integer, :default => 6
4 - settings_items :prioritize_profiles_with_image, :type => :boolean, :default => false 4 + settings_items :prioritize_profiles_with_image, :type => :boolean, :default => true
5 5
6 def self.description 6 def self.description
7 _('Random profiles') 7 _('Random profiles')
@@ -13,21 +13,21 @@ class ProfileListBlock &lt; Block @@ -13,21 +13,21 @@ class ProfileListBlock &lt; Block
13 end 13 end
14 14
15 def profile_list 15 def profile_list
16 - profiles.visible.all(:limit => limit, :select => 'DISTINCT profiles.*, ' + image_prioritizer + randomizer, :joins => "LEFT OUTER JOIN images ON images.owner_id = profiles.id", :order => image_prioritizer + randomizer) 16 + result = nil
  17 + if !prioritize_profiles_with_image
  18 + result = profiles.visible.all(:limit => limit, :order => 'updated_at DESC').sort_by{ rand }
  19 + elsif profiles.visible.with_image.count >= limit
  20 + result = profiles.visible.with_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand }
  21 + else
  22 + result = profiles.visible.with_image.sort_by{ rand } + profiles.visible.without_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand }
  23 + end
  24 + result.slice(0..limit-1)
17 end 25 end
18 26
19 def profile_count 27 def profile_count
20 profiles.visible.count('DISTINCT(profiles.id)') 28 profiles.visible.count('DISTINCT(profiles.id)')
21 end 29 end
22 30
23 - def randomizer  
24 - @randomizer ||= "(profiles.id % #{rand(profile_count) + 1})"  
25 - end  
26 -  
27 - def image_prioritizer  
28 - prioritize_profiles_with_image ? '(images.id is null),' : ''  
29 - end  
30 -  
31 # the title of the block. Probably will be overriden in subclasses. 31 # the title of the block. Probably will be overriden in subclasses.
32 def default_title 32 def default_title
33 _('{#} People or Groups') 33 _('{#} People or Groups')
db/migrate/20110526201202_move_reference_from_image_to_owners.rb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +class MoveReferenceFromImageToOwners < ActiveRecord::Migration
  2 + def self.up
  3 + %w[ profiles categories products tasks ].each do |table|
  4 + type = table.singularize.camelcase
  5 + add_column table, :image_id, :integer
  6 + update("update #{table} set image_id = (select i.id from images i where i.owner_id = #{table}.id and i.owner_type = '#{type}' limit 1) where id in (select owner_id from images where owner_type = '#{type}' and owner_id is not null)")
  7 + end
  8 + remove_column :images, :owner_id
  9 + remove_column :images, :owner_type
  10 + end
  11 +
  12 + def self.down
  13 + add_column :images, :owner_id, :integer
  14 + add_column :images, :owner_type, :string
  15 + %w[ profiles products categories tasks ].each do |table|
  16 + type = table.singularize.camelcase
  17 + update("update images set owner_id = (select id from #{table} origin where origin.image_id = images.id), owner_type = '#{type}' where id in (select image_id from #{table} where image_id is not null)")
  18 + remove_column table, :image_id
  19 + end
  20 + end
  21 +end
db/migrate/20110527042608_remove_unused_column_from_products.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class RemoveUnusedColumnFromProducts < ActiveRecord::Migration
  2 + def self.up
  3 + remove_column :products, :image
  4 + end
  5 +
  6 + def self.down
  7 + add_column :products, :image, :string
  8 + end
  9 +end
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 # 9 #
10 # It's strongly recommended to check this file into your version control system. 10 # It's strongly recommended to check this file into your version control system.
11 11
12 -ActiveRecord::Schema.define(:version => 20110524151137) do 12 +ActiveRecord::Schema.define(:version => 20110527042608) do
13 13
14 create_table "action_tracker", :force => true do |t| 14 create_table "action_tracker", :force => true do |t|
15 t.integer "user_id" 15 t.integer "user_id"
@@ -165,6 +165,7 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do @@ -165,6 +165,7 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do
165 t.boolean "display_in_menu", :default => false 165 t.boolean "display_in_menu", :default => false
166 t.integer "children_count", :default => 0 166 t.integer "children_count", :default => 0
167 t.boolean "accept_products", :default => true 167 t.boolean "accept_products", :default => true
  168 + t.integer "image_id"
168 end 169 end
169 170
170 create_table "categories_profiles", :id => false, :force => true do |t| 171 create_table "categories_profiles", :id => false, :force => true do |t|
@@ -270,8 +271,6 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do @@ -270,8 +271,6 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do
270 end 271 end
271 272
272 create_table "images", :force => true do |t| 273 create_table "images", :force => true do |t|
273 - t.string "owner_type"  
274 - t.integer "owner_id"  
275 t.integer "parent_id" 274 t.integer "parent_id"
276 t.string "content_type" 275 t.string "content_type"
277 t.string "filename" 276 t.string "filename"
@@ -339,7 +338,6 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do @@ -339,7 +338,6 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do
339 t.string "name" 338 t.string "name"
340 t.decimal "price" 339 t.decimal "price"
341 t.text "description" 340 t.text "description"
342 - t.string "image"  
343 t.datetime "created_at" 341 t.datetime "created_at"
344 t.datetime "updated_at" 342 t.datetime "updated_at"
345 t.float "lat" 343 t.float "lat"
@@ -348,6 +346,7 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do @@ -348,6 +346,7 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do
348 t.boolean "available", :default => true 346 t.boolean "available", :default => true
349 t.boolean "highlighted" 347 t.boolean "highlighted"
350 t.integer "unit_id" 348 t.integer "unit_id"
  349 + t.integer "image_id"
351 end 350 end
352 351
353 add_index "products", ["enterprise_id"], :name => "index_products_on_enterprise_id" 352 add_index "products", ["enterprise_id"], :name => "index_products_on_enterprise_id"
@@ -378,6 +377,7 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do @@ -378,6 +377,7 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do
378 t.integer "preferred_domain_id" 377 t.integer "preferred_domain_id"
379 t.datetime "updated_at" 378 t.datetime "updated_at"
380 t.boolean "visible", :default => true 379 t.boolean "visible", :default => true
  380 + t.integer "image_id"
381 end 381 end
382 382
383 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id" 383 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id"
@@ -456,6 +456,7 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do @@ -456,6 +456,7 @@ ActiveRecord::Schema.define(:version =&gt; 20110524151137) do
456 t.string "type" 456 t.string "type"
457 t.datetime "created_at" 457 t.datetime "created_at"
458 t.string "target_type" 458 t.string "target_type"
  459 + t.integer "image_id"
459 end 460 end
460 461
461 create_table "thumbnails", :force => true do |t| 462 create_table "thumbnails", :force => true do |t|
features/step_definitions/noosfero_steps.rb
@@ -116,8 +116,8 @@ Given /^the following products?$/ do |table| @@ -116,8 +116,8 @@ Given /^the following products?$/ do |table|
116 data = item.dup 116 data = item.dup
117 owner = Enterprise[data.delete("owner")] 117 owner = Enterprise[data.delete("owner")]
118 category = Category.find_by_slug(data.delete("category").to_slug) 118 category = Category.find_by_slug(data.delete("category").to_slug)
119 - product = Product.create!(data.merge(:enterprise => owner, :product_category => category))  
120 - image = Image.create!(:owner => product, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')) 119 + img = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  120 + product = Product.create!(data.merge(:enterprise => owner, :product_category => category, :image_id => img.id))
121 end 121 end
122 end 122 end
123 123
lib/acts_as_having_image.rb
@@ -2,7 +2,9 @@ module ActsAsHavingImage @@ -2,7 +2,9 @@ module ActsAsHavingImage
2 2
3 module ClassMethods 3 module ClassMethods
4 def acts_as_having_image 4 def acts_as_having_image
5 - has_one :image, :as => 'owner' 5 + belongs_to :image
  6 + named_scope :with_image, :conditions => [ "#{table_name}.image_id IS NOT NULL" ]
  7 + named_scope :without_image, :conditions => [ "#{table_name}.image_id IS NULL" ]
6 self.send(:include, ActsAsHavingImage) 8 self.send(:include, ActsAsHavingImage)
7 end 9 end
8 end 10 end
test/factories.rb
@@ -311,6 +311,7 @@ module Noosfero::Factory @@ -311,6 +311,7 @@ module Noosfero::Factory
311 end 311 end
312 312
313 alias :defaults_for_blog_archives_block :defaults_for_block 313 alias :defaults_for_blog_archives_block :defaults_for_block
  314 + alias :defaults_for_profile_list_block :defaults_for_block
314 315
315 ############################################### 316 ###############################################
316 # Task 317 # Task
test/unit/image_test.rb
@@ -22,7 +22,8 @@ class ImageTest &lt; Test::Unit::TestCase @@ -22,7 +22,8 @@ class ImageTest &lt; Test::Unit::TestCase
22 end 22 end
23 23
24 should 'create thumbnails after processing jobs' do 24 should 'create thumbnails after processing jobs' do
25 - file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :owner => profile) 25 + file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  26 + profile.update_attribute(:image_id, file.id)
26 27
27 process_delayed_job_queue 28 process_delayed_job_queue
28 Image.attachment_options[:thumbnails].each do |suffix, size| 29 Image.attachment_options[:thumbnails].each do |suffix, size|
@@ -32,7 +33,8 @@ class ImageTest &lt; Test::Unit::TestCase @@ -32,7 +33,8 @@ class ImageTest &lt; Test::Unit::TestCase
32 end 33 end
33 34
34 should 'set thumbnails_processed to true after creating thumbnails' do 35 should 'set thumbnails_processed to true after creating thumbnails' do
35 - file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :owner => profile) 36 + file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  37 + profile.update_attribute(:image_id, file.id)
36 38
37 process_delayed_job_queue 39 process_delayed_job_queue
38 40
@@ -62,7 +64,8 @@ class ImageTest &lt; Test::Unit::TestCase @@ -62,7 +64,8 @@ class ImageTest &lt; Test::Unit::TestCase
62 end 64 end
63 65
64 should 'return image thumbnail if thumbnails were processed' do 66 should 'return image thumbnail if thumbnails were processed' do
65 - file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :owner => profile) 67 + file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  68 + profile.update_attribute(:image_id, file.id)
66 process_delayed_job_queue 69 process_delayed_job_queue
67 70
68 assert_match(/rails_thumb.png/, Image.find(file.id).public_filename(:thumb)) 71 assert_match(/rails_thumb.png/, Image.find(file.id).public_filename(:thumb))
@@ -71,7 +74,8 @@ class ImageTest &lt; Test::Unit::TestCase @@ -71,7 +74,8 @@ class ImageTest &lt; Test::Unit::TestCase
71 end 74 end
72 75
73 should 'store width and height after processing' do 76 should 'store width and height after processing' do
74 - file = Image.create!(:owner => profile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')) 77 + file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  78 + profile.update_attribute(:image_id, file.id)
75 file.create_thumbnails 79 file.create_thumbnails
76 80
77 file = Image.find(file.id) 81 file = Image.find(file.id)
@@ -89,7 +93,7 @@ class ImageTest &lt; Test::Unit::TestCase @@ -89,7 +93,7 @@ class ImageTest &lt; Test::Unit::TestCase
89 # this test verifies whether it created background jobs also for the 93 # this test verifies whether it created background jobs also for the
90 # thumbnails! 94 # thumbnails!
91 assert_no_difference Delayed::Job, :count do 95 assert_no_difference Delayed::Job, :count do
92 - image = Image.new(:owner => profile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')) 96 + image = Image.new(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
93 image.stubs(:is_thumbnail?).returns(true) 97 image.stubs(:is_thumbnail?).returns(true)
94 image.save! 98 image.save!
95 end 99 end
@@ -97,7 +101,8 @@ class ImageTest &lt; Test::Unit::TestCase @@ -97,7 +101,8 @@ class ImageTest &lt; Test::Unit::TestCase
97 101
98 should 'upload to a folder with same name as the schema if database is postgresql' do 102 should 'upload to a folder with same name as the schema if database is postgresql' do
99 uses_postgresql 103 uses_postgresql
100 - file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :owner => profile) 104 + file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  105 + profile.update_attribute(:image_id, file.id)
101 process_delayed_job_queue 106 process_delayed_job_queue
102 assert_match(/images\/test_schema\/\d{4}\/\d{4}\/rails.png/, Image.find(file.id).public_filename) 107 assert_match(/images\/test_schema\/\d{4}\/\d{4}\/rails.png/, Image.find(file.id).public_filename)
103 file.destroy 108 file.destroy
@@ -106,7 +111,8 @@ class ImageTest &lt; Test::Unit::TestCase @@ -106,7 +111,8 @@ class ImageTest &lt; Test::Unit::TestCase
106 111
107 should 'upload to path prefix folder if database is not postgresql' do 112 should 'upload to path prefix folder if database is not postgresql' do
108 uses_sqlite 113 uses_sqlite
109 - file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :owner => profile) 114 + file = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  115 + profile.update_attribute(:image_id, file.id)
110 process_delayed_job_queue 116 process_delayed_job_queue
111 assert_match(/images\/\d{4}\/\d{4}\/rails.png/, Image.find(file.id).public_filename) 117 assert_match(/images\/\d{4}\/\d{4}\/rails.png/, Image.find(file.id).public_filename)
112 file.destroy 118 file.destroy
test/unit/profile_list_block_test.rb
@@ -137,50 +137,80 @@ class ProfileListBlockTest &lt; Test::Unit::TestCase @@ -137,50 +137,80 @@ class ProfileListBlockTest &lt; Test::Unit::TestCase
137 137
138 should 'list random profiles' do 138 should 'list random profiles' do
139 env = fast_create(Environment) 139 env = fast_create(Environment)
140 - p1 = fast_create(Person, :environment_id => env.id)  
141 - p2 = fast_create(Person, :environment_id => env.id)  
142 - p3 = fast_create(Person, :environment_id => env.id) 140 + 6.times.each do
  141 + fast_create(Person, :environment_id => env.id)
  142 + end
143 143
144 block = ProfileListBlock.new 144 block = ProfileListBlock.new
145 block.stubs(:owner).returns(env) 145 block.stubs(:owner).returns(env)
146 146
147 - # force the "random" function to return something we know  
148 - block.stubs(:randomizer).returns('-profiles.id')  
149 -  
150 - assert_equal [p3.id, p2.id, p1.id], block.profile_list.map(&:id) 147 + assert_not_equal block.profile_list.map(&:id), block.profile_list.map(&:id)
151 end 148 end
152 149
153 - should 'randomize using modulo operator and random number' do  
154 - block = ProfileListBlock.new  
155 - block.expects(:profile_count).returns(10)  
156 - block.expects(:rand).with(10).returns(5)  
157 - assert_match /profiles.id % 6/, block.randomizer 150 + should 'prioritize profiles with image if this option is turned on' do
  151 + env = fast_create(Environment)
  152 + img1 = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  153 + p1 = fast_create(Person, :environment_id => env.id, :image_id => img1.id)
  154 + img2 = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  155 + p2 = fast_create(Person, :environment_id => env.id, :image_id => img2.id)
  156 +
  157 + p_without_image = fast_create(Person, :environment_id => env.id)
  158 +
  159 + block = ProfileListBlock.new(:limit => 2)
  160 + block.stubs(:owner).returns(env)
  161 + block.stubs(:prioritize_profiles_with_image).returns(true)
  162 +
  163 + assert_not_includes block.profile_list[0..1], p_without_image
158 end 164 end
159 165
160 - should 'not divide by zero' do 166 + should 'list profiles without image only if profiles with image arent enought' do
  167 + env = fast_create(Environment)
  168 + img1 = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  169 + p1 = fast_create(Person, :environment_id => env.id, :image_id => img1.id)
  170 + img2 = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  171 + p2 = fast_create(Person, :environment_id => env.id, :image_id => img2.id)
  172 + p_without_image = fast_create(Person, :environment_id => env.id)
161 block = ProfileListBlock.new 173 block = ProfileListBlock.new
162 - block.stubs(:profile_count).returns(0)  
163 - block.expects(:rand).returns(0)  
164 - assert_no_match /profiles.id % 0/, block.randomizer 174 + block.stubs(:owner).returns(env)
  175 + block.stubs(:prioritize_profiles_with_image).returns(true)
  176 +
  177 + block.limit = 2
  178 + assert_not_includes block.profile_list, p_without_image
  179 +
  180 + block.limit = 3
  181 + assert_includes block.profile_list, p_without_image
165 end 182 end
166 183
167 - should 'prioritize profiles with image if this option is turned on' do 184 + should 'list profile with image among profiles without image' do
168 env = fast_create(Environment) 185 env = fast_create(Environment)
169 - p1 = fast_create(Person, :environment_id => env.id)  
170 - img1 = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :owner => p1)  
171 - p2 = fast_create(Person, :environment_id => env.id)  
172 - img2 = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :owner => p2) 186 + 5.times do |n|
  187 + fast_create(Person, :environment_id => env.id)
  188 + end
  189 + img = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  190 + with_image = fast_create(Person, :environment_id => env.id, :image_id => img.id)
  191 + block = ProfileListBlock.new(:limit => 3)
  192 + block.stubs(:prioritize_profiles_with_image).returns(true)
  193 + block.stubs(:owner).returns(env)
  194 + assert_includes block.profile_list, with_image
  195 + end
173 196
174 - p_without_image = fast_create(Person, :environment_id => env.id) 197 + should 'not prioritize profiles with image if this option is turned off' do
  198 + env = fast_create(Environment)
  199 + img = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  200 + with_image = fast_create(Person, :environment_id => env.id, :updated_at => DateTime.now, :image_id => img.id)
  201 + 5.times do |n|
  202 + fast_create(Person, :environment_id => env.id, :updated_at => DateTime.now + 1.day)
  203 + end
175 204
176 - block = ProfileListBlock.new 205 + block = ProfileListBlock.new(:limit => 3)
177 block.stubs(:owner).returns(env) 206 block.stubs(:owner).returns(env)
178 - block.stubs(:prioritize_profiles_with_image).returns(true) 207 + block.stubs(:prioritize_profiles_with_image).returns(false)
179 208
180 - # force the "random" function to return something we know  
181 - block.stubs(:randomizer).returns('-profiles.id') 209 + assert_not_includes block.profile_list, with_image
  210 + end
182 211
183 - assert_not_includes block.profile_list[0..1], p_without_image 212 + should 'prioritize profiles with image by default' do
  213 + assert ProfileListBlock.new.prioritize_profiles_with_image
184 end 214 end
185 215
186 end 216 end
test/unit/profile_test.rb
@@ -1357,12 +1357,6 @@ class ProfileTest &lt; Test::Unit::TestCase @@ -1357,12 +1357,6 @@ class ProfileTest &lt; Test::Unit::TestCase
1357 assert !profile.folders.include?(child) 1357 assert !profile.folders.include?(child)
1358 end 1358 end
1359 1359
1360 - should 'validates profile image when save' do  
1361 - profile = build(Profile, :image_builder => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')})  
1362 - profile.image.expects(:valid?).returns(false).at_least_once  
1363 - assert !profile.valid?  
1364 - end  
1365 -  
1366 should 'profile is invalid when image not valid' do 1360 should 'profile is invalid when image not valid' do
1367 profile = build(Profile, :image_builder => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')}) 1361 profile = build(Profile, :image_builder => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')})
1368 profile.image.expects(:valid?).returns(false).at_least_once 1362 profile.image.expects(:valid?).returns(false).at_least_once
@@ -1710,6 +1704,28 @@ class ProfileTest &lt; Test::Unit::TestCase @@ -1710,6 +1704,28 @@ class ProfileTest &lt; Test::Unit::TestCase
1710 assert profile.is_on_homepage?("/#{profile.identifier}/#{homepage.slug}", homepage) 1704 assert profile.is_on_homepage?("/#{profile.identifier}/#{homepage.slug}", homepage)
1711 end 1705 end
1712 1706
  1707 + should 'find profiles with image' do
  1708 + env = fast_create(Environment)
  1709 + 2.times do |n|
  1710 + img = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  1711 + fast_create(Person, :name => "with_image_#{n}", :environment_id => env.id, :image_id => img.id)
  1712 + end
  1713 + without_image = fast_create(Person, :name => 'without_image', :environment_id => env.id)
  1714 + assert_equal 2, env.profiles.with_image.count
  1715 + assert_not_includes env.profiles.with_image, without_image
  1716 + end
  1717 +
  1718 + should 'find profiles withouth image' do
  1719 + env = fast_create(Environment)
  1720 + 2.times do |n|
  1721 + fast_create(Person, :name => "without_image_#{n}", :environment_id => env.id)
  1722 + end
  1723 + img = Image.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  1724 + with_image = fast_create(Person, :name => 'with_image', :environment_id => env.id, :image_id => img.id)
  1725 + assert_equal 2, env.profiles.without_image.count
  1726 + assert_not_includes env.profiles.without_image, with_image
  1727 + end
  1728 +
1713 private 1729 private
1714 1730
1715 def assert_invalid_identifier(id) 1731 def assert_invalid_identifier(id)