├── install.rb ├── uninstall.rb ├── spec ├── rcov.opts ├── spec.opts ├── fixtures │ └── correct_jpeg_image.jpg ├── spec_helper.rb ├── active_record_extensions │ └── has_attached_file_spec.rb └── active_record_extensions_spec.rb ├── lib ├── oz_lib.rb ├── active_record_extensions │ └── has_attached_file.rb └── active_record_extensions.rb ├── init.rb ├── README ├── Rakefile └── MIT-LICENSE /install.rb: -------------------------------------------------------------------------------- 1 | # Install hook code here 2 | -------------------------------------------------------------------------------- /uninstall.rb: -------------------------------------------------------------------------------- 1 | # Uninstall hook code here 2 | -------------------------------------------------------------------------------- /spec/rcov.opts: -------------------------------------------------------------------------------- 1 | --exclude "spec/*,gems/*" 2 | --rails -------------------------------------------------------------------------------- /lib/oz_lib.rb: -------------------------------------------------------------------------------- 1 | # OzLib 2 | 3 | require 'active_record_extensions' 4 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --colour 2 | --format progress 3 | --loadby mtime 4 | --reverse 5 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | # Include hook code here 2 | require File.join(File.dirname(__FILE__), 'lib/oz_lib') 3 | -------------------------------------------------------------------------------- /spec/fixtures/correct_jpeg_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/html/oz_lib/master/spec/fixtures/correct_jpeg_image.jpg -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | OzLib 2 | ===== 3 | 4 | Introduction goes here. 5 | 6 | 7 | Example 8 | ======= 9 | 10 | Example goes here. 11 | 12 | 13 | Copyright (c) 2010 [name of plugin creator], released under the MIT license 14 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | require 'rake/rdoctask' 4 | 5 | desc 'Default: run unit tests.' 6 | task :default => :test 7 | 8 | desc 'Test the oz_lib plugin.' 9 | Rake::TestTask.new(:test) do |t| 10 | t.libs << 'lib' 11 | t.libs << 'test' 12 | t.pattern = 'test/**/*_test.rb' 13 | t.verbose = true 14 | end 15 | 16 | desc 'Generate documentation for the oz_lib plugin.' 17 | Rake::RDocTask.new(:rdoc) do |rdoc| 18 | rdoc.rdoc_dir = 'rdoc' 19 | rdoc.title = 'OzLib' 20 | rdoc.options << '--line-numbers' << '--inline-source' 21 | rdoc.rdoc_files.include('README') 22 | rdoc.rdoc_files.include('lib/**/*.rb') 23 | end 24 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 [name of plugin creator] 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to ~/spec when you run 'ruby script/generate rspec' 2 | # from the project root directory. 3 | require 'rubygems' 4 | gem :rails, :version => '2.3.5' 5 | require File.dirname(__FILE__) + '/../init.rb' 6 | #require 'active_record' 7 | #require 'active_support' 8 | #require 'active_support/test_case' 9 | #require 'action_pack' 10 | require 'action_controller' 11 | require 'action_controller/test_process' 12 | #require 'spec/autorun' 13 | #require 'spec/rails' 14 | require 'shoulda' 15 | require 'machinist/active_record' 16 | require 'ruby-debug' 17 | require 'ostruct' 18 | 19 | # Uncomment the next line to use webrat's matchers 20 | #require 'webrat/integrations/rspec-rails' 21 | 22 | # Requires supporting files with custom matchers and macros, etc, 23 | # in ./support/ and its subdirectories. 24 | Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f} 25 | 26 | FIXTURES_PATH = File.dirname(__FILE__) + '/fixtures/' 27 | 28 | Spec::Runner.configure do |config| 29 | # If you're not using ActiveRecord you should remove these 30 | # lines, delete config/database.yml and disable :active_record 31 | # in your config/boot.rb 32 | #config.use_transactional_fixtures = true 33 | #config.use_instantiated_fixtures = false 34 | 35 | # == Fixtures 36 | # 37 | # You can declare fixtures for each example_group like this: 38 | # describe "...." do 39 | # fixtures :table_a, :table_b 40 | # 41 | # Alternatively, if you prefer to declare them only once, you can 42 | # do so right here. Just uncomment the next line and replace the fixture 43 | # names with your fixtures. 44 | # 45 | # config.global_fixtures = :table_a, :table_b 46 | # 47 | # If you declare global fixtures, be aware that they will be declared 48 | # for all of your examples, even those that don't use them. 49 | # 50 | # You can also declare which fixtures to use (for example fixtures for test/fixtures): 51 | # 52 | # 53 | # == Mock Framework 54 | # 55 | # RSpec uses its own mocking framework by default. If you prefer to 56 | # use mocha, flexmock or RR, uncomment the appropriate line: 57 | # 58 | config.mock_with :mocha 59 | # config.mock_with :flexmock 60 | # config.mock_with :rr 61 | # 62 | # == Notes 63 | # 64 | # For more information take a look at Spec::Runner::Configuration and Spec::Runner 65 | 66 | def fixture_file_upload(path, mime_type = nil, binary = false) 67 | ActionController::TestUploadedFile.new( 68 | FIXTURES_PATH + path, 69 | mime_type, 70 | binary 71 | ) 72 | end 73 | end 74 | 75 | def suppress_stdout(&block) 76 | orig_stdout = $stdout 77 | $stdout = StringIO.new 78 | 79 | yield 80 | 81 | $stdout = orig_stdout 82 | end 83 | -------------------------------------------------------------------------------- /lib/active_record_extensions/has_attached_file.rb: -------------------------------------------------------------------------------- 1 | # 2 | # © Copyright 2010 Olexiy Zamkoviy. All Rights Reserved. 3 | # 4 | 5 | module ActiveRecordExtensions 6 | module HasAttachedFile 7 | def has_attached_file(file) 8 | unless respond_to?(:blob_owner_id) 9 | define_method 'blob_owner_id' do 10 | raise StandardError, "You must define blob_owner_id method returning correct blob owner id before has_attached_file invoking" 11 | end 12 | end 13 | 14 | file_id = "#{file}_id" 15 | file_size = "#{file}_size" 16 | blob_file_object = "blob_#{file}_object" 17 | 18 | unless column_names.include?(file_id) 19 | raise StandardError, "#{to_s} should have #{file_id} column" 20 | end 21 | 22 | attr_reader file 23 | attr_reader "#{file}_file_object" 24 | 25 | define_method "#{file}=" do |f| 26 | instance_variable_set("@#{file}_file_object", f) 27 | instance_variable_set("@#{file}", Blob.new( 28 | :blob_owner_id => blob_owner_id, 29 | :content_type => f.content_type, 30 | :name => f.original_filename, 31 | :file => RUBY_VERSION == '1.9.1' ? [f.read].pack('m') : Base64.encode64(f.read) 32 | )) 33 | end 34 | 35 | define_method file_size do 36 | begin 37 | if send(file) 38 | File.size(send(file)) 39 | elsif send(file_id) 40 | send(blob_file_object).size 41 | end 42 | rescue 43 | nil 44 | end 45 | end 46 | 47 | define_method blob_file_object do 48 | begin 49 | var = "@#{blob_file_object}" 50 | 51 | unless instance_variable_get(var) 52 | instance_variable_set(var, Blob.find(send(file_id))) 53 | end 54 | 55 | instance_variable_get(var) 56 | rescue 57 | nil 58 | end 59 | end 60 | 61 | 62 | before_save do |item| 63 | f = item.send(file) 64 | 65 | if f && item.valid? && (!(f.id && f.id == item.send("#{file}_id")) && f.save) 66 | if item.send("#{file}_id") 67 | begin 68 | Blob.find(item.send("#{file}_id")).destroy 69 | rescue 70 | end 71 | end 72 | 73 | item.send("#{file}_id=", f.id) 74 | elsif f && f.errors.errors 75 | f.errors.full_messages.each do |message| 76 | item.errors.add(file, message) 77 | end 78 | 79 | false 80 | end 81 | end 82 | end 83 | 84 | def validates_attachment_presence(file) 85 | #TODO 86 | validates_presence_of file 87 | end 88 | 89 | def validates_attachment_content_type(*args) 90 | #TODO 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/active_record_extensions.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # 3 | # © Copyright 2010 Olexiy Zamkoviy. All Rights Reserved. 4 | # 5 | 6 | require 'active_record' 7 | require 'active_record_extensions/has_attached_file' 8 | 9 | ActiveRecord::Base.class_eval do 10 | ALNUM_RE = /\A[a-zA-Z0-9][-a-zA-Z0-9 ]+\z/u 11 | IDENTIFIER_RE = /\A[a-zA-Z][a-zA-Z0-9]*\z/u 12 | 13 | # TODO: add tests 14 | ALNUM_G_RE = /\A[-#{"\xC3\x80-\xC3\x96\xC3\x98-\xC3\xB6\xC3\xB8-\xE1\xBF\xBE"}a-zA-Z0-9 ]+\z/u 15 | ALNUM_G_EXTENDED_RE = /\A[-#{"\xC3\x80-\xC3\x96\xC3\x98-\xC3\xB6\xC3\xB8-\xE1\xBF\xBE"}a-zA-Z0-9 '`’]+\z/u 16 | IDENTIFIER_G_RE = /\A[a-zA-Z][-#{"\xC3\x80-\xC3\x96\xC3\x98-\xC3\xB6\xC3\xB8-\xE1\xBF\xBE"}a-zA-Z0-9]+\z/u 17 | 18 | IDENTIFIER_LIST_RE = /\A([a-zA-Z][a-zA-Z0-9]{,99})(,[a-zA-Z][a-zA-Z0-9]{,99}){,9}\z/ 19 | IDENTIFIER_LIST_G_RE = /\A([a-zA-Z][-#{"\xC3\x80-\xC3\x96\xC3\x98-\xC3\xB6\xC3\xB8-\xE1\xBF\xBE"}a-zA-Z0-9]+,)*[a-zA-Z][-#{"\xC3\x80-\xC3\x96\xC3\x98-\xC3\xB6\xC3\xB8-\xE1\xBF\xBE"}a-zA-Z0-9]+\z/u 20 | URL_RE = /\A(?#Proto)(https?\:\/\/)?(?#Subdomains)([-#{"\xC3\x80-\xC3\x96\xC3\x98-\xC3\xB6\xC3\xB8-\xE1\xBF\xBE"}\w]+\.)+(?#TopLevelDomain)[#{"\xC3\x80-\xC3\x96\xC3\x98-\xC3\xB6\xC3\xB8-\xE1\xBF\xBE"}A-Za-z]{2,}(?#TheRest)\/?[^\r\n]*\z/u 21 | 22 | def self.validates_as_alnum(field) 23 | validates_format_of field, :with => ALNUM_RE, :message => "should contain at least two symbols and begin with a letter", :if => lambda { |m| m.errors.on(field).nil? } 24 | end 25 | 26 | def self.validates_as_alnum_g(field) 27 | validates_format_of field, :with => ALNUM_G_RE, :message => "should contain letters, spaces, dashes or numbers and begin from letter or number", :if => lambda { |m| m.errors.on(field).nil? } 28 | end 29 | 30 | def self.validates_as_url(field) 31 | validates_format_of field, :with => URL_RE, :message => "should be valid url", :if => lambda { |m| m.errors.on(field).nil? } 32 | end 33 | 34 | # 35 | # Same as validates_as_alnum_g but also allows "`" and "'" symbols 36 | # 37 | def self.validates_as_alnum_g_extended(field) 38 | validates_format_of field, :with => ALNUM_G_EXTENDED_RE, :message => "contains wrong characters", :if => lambda { |m| m.errors.on(field).nil? } 39 | end 40 | 41 | def self.validates_as_identifier_list_g(field) 42 | validates_format_of field, :with => IDENTIFIER_LIST_G_RE, :message => "is invalid", :if => lambda { |m| m.errors.on(field).nil? } 43 | end 44 | 45 | def self.validates_as_identifier_list(field) 46 | validates_format_of field, :with => IDENTIFIER_LIST_RE, :message => "is invalid", :if => lambda { |m| m.errors.on(field).nil? } 47 | end 48 | 49 | def self.default_value_for(key, val) 50 | before_validation :on => :create do |item| 51 | unless item.send(key) # unless email_action_type 52 | item.send("#{key}=", val) # self.email_action_type = 'none' 53 | end # end 54 | 55 | true 56 | end 57 | end 58 | 59 | def self.only_jpeg_or_png_images_allowed_for(column) 60 | lambda do |item| 61 | if item.send(column) 62 | unless item.send(column).content_type.match /image\/(?:p?jpe?g|(?:x-)?png)/ 63 | item.errors.add(column, "should be either png or jpeg image") 64 | end 65 | end 66 | end 67 | end 68 | 69 | def self.max_size_of(column, size_val, size_name) 70 | lambda do |item| 71 | file = item.send("#{column}_file_object") 72 | if file 73 | size = file.respond_to?(:size) ? file.size : File.size(file) 74 | if size > size_val.send(size_name.downcase) 75 | item.errors.add(column, "size should be less than #{size_val} #{size_name}") 76 | end 77 | end 78 | end 79 | end 80 | 81 | def self.max_items_count(max_count, options = {}) 82 | lambda do |item| 83 | count = 84 | if options[:scope] 85 | if respond_to?(options[:scope]) 86 | send(options[:scope]).count 87 | else 88 | self.count(:conditions => { options[:scope] => item.send(options[:scope]) }) 89 | end 90 | else 91 | self.count 92 | end 93 | 94 | if count >= max_count 95 | item.errors.add(:base, "Max items count exceeded, can't add another one") 96 | end 97 | end 98 | end 99 | 100 | extend ActiveRecordExtensions::HasAttachedFile 101 | end 102 | -------------------------------------------------------------------------------- /spec/active_record_extensions/has_attached_file_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # © Copyright 2010 Olexiy Zamkoviy. All Rights Reserved. 3 | # 4 | 5 | require 'spec_helper' 6 | 7 | describe "has_attached_file extension" do 8 | before :all do 9 | suppress_stdout do 10 | ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:") 11 | ActiveRecord::Schema.define do 12 | create_table :test_models do |t| 13 | t.string :title 14 | t.column :file_id, :integer 15 | end 16 | end 17 | end 18 | 19 | ActiveRecordExtensions::HasAttachedFile.module_eval do 20 | unless defined?(Blob) 21 | Blob = OpenStruct 22 | end 23 | end 24 | end 25 | 26 | context "when enabled" do 27 | before :all do 28 | class ::TestModel1 < ActiveRecord::Base 29 | set_table_name :test_models 30 | has_attached_file :file 31 | 32 | def blob_owner_id 33 | 11111 34 | end 35 | end 36 | 37 | @model = TestModel1 38 | end 39 | 40 | before :each do 41 | @blob_identifier = rand 42 | end 43 | 44 | it "should have file getter" do 45 | @model.new.should respond_to :file 46 | end 47 | 48 | it "should have file setter" do 49 | @model.new.should respond_to :file= 50 | end 51 | 52 | context "creating new instance with file set" do 53 | before :each do 54 | @upload_file = fixture_file_upload('correct_jpeg_image.jpg', 'image/jpg') 55 | @row = @model.new :file => @upload_file 56 | end 57 | 58 | it "should have correct file attribute" do 59 | @row.file.should_not be_nil 60 | @row.file.blob_owner_id.should == 11111 61 | @row.file.content_type.should == 'image/jpg' 62 | @row.file.name.should == @upload_file.original_filename 63 | #XXX this does not work seems that fixture files act tricky 64 | false && @row.file.file.should == @upload_file.read 65 | end 66 | end 67 | 68 | context "on saving" do 69 | before :each do 70 | @upload_file = fixture_file_upload('correct_jpeg_image.jpg', 'image/jpg') 71 | @row = @model.new :file => @upload_file 72 | end 73 | 74 | context "without file" do 75 | before :each do 76 | @row.stubs(:file).returns(nil) 77 | end 78 | 79 | it "should do nothing" do 80 | @row.expects(:file_id=).never 81 | @row.save 82 | end 83 | end 84 | 85 | context "invalid object" do 86 | before :all do 87 | class ::TestModel4 < ActiveRecord::Base 88 | validates_presence_of :title 89 | set_table_name :test_models 90 | has_attached_file :file 91 | 92 | def blob_owner_id 93 | 11111 94 | end 95 | end 96 | 97 | @model = TestModel4 98 | end 99 | 100 | it "should do nothing when there are no file errors" do 101 | @model.any_instance.expects(:file_id=).never 102 | @row = @model.new :file => fixture_file_upload('correct_jpeg_image.jpg', 'image/jpg') 103 | @row.save 104 | @row.file_id.should be_nil 105 | end 106 | end 107 | 108 | context "valid object" do 109 | context "with not saved file object" do 110 | it "should change object's file_id" do 111 | @row = @model.new :file => fixture_file_upload('correct_jpeg_image.jpg', 'image/jpg') 112 | 113 | @row.file.expects(:save).returns(true).at_least(1) 114 | @model.any_instance.expects(:file_id=).at_least(1) 115 | 116 | @row.save 117 | end 118 | end 119 | 120 | context "with saved file object" do 121 | it "should not change object's file_id" do 122 | @row = @model.new :file => fixture_file_upload('correct_jpeg_image.jpg', 'image/jpg') 123 | @row.file_id = @row.file.id = 1 124 | @row.file.errors = stub(:errors) 125 | @row.save 126 | 127 | @row.file.expects(:save).never 128 | @model.any_instance.expects(:file_id=).never 129 | 130 | @row.save 131 | end 132 | end 133 | 134 | context "with invalid file object" do 135 | it "should not save record and add validation errors if there are any errors for file" do 136 | @model.any_instance.expects(:file_id=).never 137 | @row = @model.new :file => fixture_file_upload('correct_jpeg_image.jpg', 'image/jpg') 138 | @row.file.stubs(:errors).returns(mock(:errors => {}, :full_messages => ["Test Error"])) 139 | 140 | @row.save.should be_false 141 | @row.file_id.should be_nil 142 | @row.errors.on(:file).should == 'Test Error' 143 | end 144 | end 145 | 146 | context "with assigned file" do 147 | it "should delete old file" do 148 | @row = @model.new :file => fixture_file_upload('correct_jpeg_image.jpg', 'image/jpg'), :file_id => 3 149 | 150 | @row.file.expects(:save).returns(true).at_least(1) 151 | @model.any_instance.expects(:file_id=).at_least(1) 152 | 153 | ActiveRecordExtensions::HasAttachedFile::Blob.expects(:find).with(3).returns(mock(:destroy => nil)) 154 | 155 | @row.save 156 | end 157 | end 158 | end 159 | end 160 | end 161 | 162 | context "when enabled on model without blob_owner_id method" do 163 | before :all do 164 | class ::TestModel2 < ActiveRecord::Base 165 | set_table_name :test_models 166 | has_attached_file :file 167 | end 168 | @model = TestModel2 169 | end 170 | 171 | it "should define blob_owner_id wich raises error" do 172 | lambda do 173 | @model.new.blob_owner_id 174 | end.should raise_error(StandardError, "You must define blob_owner_id method returning correct blob owner id before has_attached_file invoking") 175 | end 176 | end 177 | 178 | context "when enabled on model without {file}_id attribute" do 179 | before :all do 180 | suppress_stdout do 181 | ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:") 182 | ActiveRecord::Schema.define do 183 | create_table :test_models do |t| 184 | end 185 | end 186 | end 187 | end 188 | 189 | 190 | it "should raise error" do 191 | lambda do 192 | class ::TestModel3 < ActiveRecord::Base 193 | set_table_name :test_models 194 | has_attached_file :file 195 | end 196 | end.should raise_error(StandardError, "TestModel3 should have file_id column") 197 | end 198 | end 199 | end 200 | 201 | -------------------------------------------------------------------------------- /spec/active_record_extensions_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # © Copyright 2010 Olexiy Zamkoviy. All Rights Reserved. 3 | # 4 | 5 | require 'spec_helper' 6 | 7 | describe "active_record_extensions" do 8 | context "IDENTIFIER_LIST_G_RE" do 9 | it { "asdf,jkl,zxcv".should match(ActiveRecord::Base.const_get("IDENTIFIER_LIST_G_RE")) } 10 | it { "asdf,jkl,zxcv,".should_not match(ActiveRecord::Base.const_get("IDENTIFIER_LIST_G_RE")) } 11 | it { "asdf ,jkl,zxcv".should_not match(ActiveRecord::Base.const_get("IDENTIFIER_LIST_G_RE")) } 12 | it { "as df,jkl,zxcv".should_not match(ActiveRecord::Base.const_get("IDENTIFIER_LIST_G_RE")) } 13 | end 14 | 15 | context "URL_RE" do 16 | context "valid cases" do 17 | it { "http://test.com/".should match(ActiveRecord::Base.const_get('URL_RE')) } 18 | it { "https://test.com/".should match(ActiveRecord::Base.const_get('URL_RE')) } 19 | it { "http://subdomain.test.com/".should match(ActiveRecord::Base.const_get('URL_RE')) } 20 | it { "http://test.com/@!@#!@!@$".should match(ActiveRecord::Base.const_get('URL_RE')) } 21 | end 22 | 23 | context "invalid cases" do 24 | it { "http://wrong domain.test.com/".should_not match(ActiveRecord::Base.const_get('URL_RE')) } 25 | it { "http://wrongdomain!.test.com/".should_not match(ActiveRecord::Base.const_get('URL_RE')) } 26 | it { "http://wrongdomain@.test.com/".should_not match(ActiveRecord::Base.const_get('URL_RE')) } 27 | it { "http://wrongdomain#.test.com/".should_not match(ActiveRecord::Base.const_get('URL_RE')) } 28 | it { "http://test/".should_not match(ActiveRecord::Base.const_get('URL_RE')) } 29 | 30 | it "ends with newline" do 31 | "http://test.com/\n".should_not match(ActiveRecord::Base.const_get('URL_RE')) 32 | end 33 | 34 | it "ends with caret return" do 35 | "http://test.com/\r".should_not match(ActiveRecord::Base.const_get('URL_RE')) 36 | end 37 | 38 | it "begins with newline" do 39 | "\nhttp://test.com/".should_not match(ActiveRecord::Base.const_get('URL_RE')) 40 | end 41 | 42 | it "begins with caret return" do 43 | "\rhttp://test.com/".should_not match(ActiveRecord::Base.const_get('URL_RE')) 44 | end 45 | 46 | it "contains newline" do 47 | "http://tes\nt.com/".should_not match(ActiveRecord::Base.const_get('URL_RE')) 48 | end 49 | 50 | it "contains caret return" do 51 | "http://tes\rt.com/".should_not match(ActiveRecord::Base.const_get('URL_RE')) 52 | end 53 | end 54 | end 55 | 56 | context "ALNUM_RE" do 57 | context "valid cases" do 58 | it { "Alnum".should match(ActiveRecord::Base.const_get('ALNUM_RE')) } 59 | it { "Alnum123".should match(ActiveRecord::Base.const_get('ALNUM_RE')) } 60 | it { "Alnum123 ".should match(ActiveRecord::Base.const_get('ALNUM_RE')) } 61 | it { "123Alnum".should match(ActiveRecord::Base.const_get('ALNUM_RE')) } 62 | end 63 | 64 | context "invalid cases" do 65 | it "begins with space" do 66 | " Alnum123".should_not match(ActiveRecord::Base.const_get('ALNUM_RE')) 67 | end 68 | 69 | it { "Alnum#".should_not match(ActiveRecord::Base.const_get('ALNUM_RE')) } 70 | it { "Alnum$".should_not match(ActiveRecord::Base.const_get('ALNUM_RE')) } 71 | 72 | it "ends with newline" do 73 | "Alnum\n".should_not match(ActiveRecord::Base.const_get('ALNUM_RE')) 74 | end 75 | 76 | it "ends with caret return" do 77 | "Alnum\r".should_not match(ActiveRecord::Base.const_get('ALNUM_RE')) 78 | end 79 | 80 | it "contains newline" do 81 | "Alnum\nxx".should_not match(ActiveRecord::Base.const_get('ALNUM_RE')) 82 | end 83 | 84 | it "contains caret return" do 85 | "Alnum\ryy".should_not match(ActiveRecord::Base.const_get('ALNUM_RE')) 86 | end 87 | end 88 | end 89 | 90 | context "IDENTIFIER_RE" do 91 | context "valid cases" do 92 | it { "Identifer".should match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) } 93 | it { "Identifier123".should match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) } 94 | end 95 | 96 | context "invalid cases" do 97 | it "begins with space" do 98 | " Identifier123".should_not match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) 99 | end 100 | 101 | it "ends with space" do 102 | "Identifier123 ".should_not match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) 103 | end 104 | 105 | it "ends with newline" do 106 | "Identifier123\n".should_not match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) 107 | end 108 | 109 | it "ends with caret return" do 110 | "Identifier123\r".should_not match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) 111 | end 112 | 113 | it "contains newline" do 114 | "Identifier123\nxx".should_not match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) 115 | end 116 | 117 | it "contains caret return" do 118 | "Identifier123\ryy".should_not match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) 119 | end 120 | 121 | it "begins with number" do 122 | "123Identifier".should_not match(ActiveRecord::Base.const_get('IDENTIFIER_RE')) 123 | end 124 | end 125 | end 126 | 127 | context "IDENTIFIER_LIST_RE" do 128 | context "valid cases" do 129 | it { ("a" * 100).should match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) } 130 | it { (("a" * 100) + ',' + ("b" * 100)).should match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) } 131 | it { (["a" * 100] * 10).join(',').should match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) } 132 | end 133 | 134 | context "invalid cases" do 135 | it { ("a" * 101).should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) } 136 | 137 | context "single item" do 138 | it "ends with newline" do 139 | ("a" * 99 + "\n").should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 140 | end 141 | 142 | it "ends with caret return" do 143 | ("a" * 99 + "\r").should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 144 | end 145 | 146 | it "contains newline" do 147 | ("a" * 50 + "\n" + "b" * 49).should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 148 | end 149 | 150 | it "contains newline" do 151 | ("a" * 50 + "\n" + "b" * 49).should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 152 | end 153 | end 154 | 155 | context "multiple items" do 156 | it "last element contains newline" do 157 | (("a" * 100) + ',' + ("b" * 99) + "\n").should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 158 | end 159 | 160 | it "last element contains caret return" do 161 | (("a" * 100) + ',' + ("b" * 99) + "\r").should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 162 | end 163 | 164 | it "first element contains newline" do 165 | (("a" * 99) + "\n" + ',' + ("b" * 100)).should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 166 | end 167 | 168 | it "first element contains caret return" do 169 | (("a" * 99) + "\r" + ',' + ("b" * 100)).should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 170 | end 171 | 172 | it "newline after all elements" do 173 | ((["a" * 100] * 10).join(',') + "\n").should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) 174 | end 175 | 176 | it { (("a" * 100) + ',' + ("b" * 101)).should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) } 177 | it { (["a" * 100] * 11).join(',').should_not match(ActiveRecord::Base.const_get('IDENTIFIER_LIST_RE')) } 178 | 179 | end 180 | end 181 | end 182 | end 183 | 184 | --------------------------------------------------------------------------------