I''ve got a model Officer that relies on password validation in all cases, but I need it to validate the password at different times. For on create, I want to verify that the password and password confirmation exists at all times. For update, want to make it possible for providing password optional, and only when the password is provided, to make sure it matches the password confirmation and the within a certain length (such as when calling method update_attributes). The best ways I see it right now is to define either before_validation_on_create, after_validation_on_create, or before_create for method new, and define either before_validation_on_update, after_validation_on_update, or before_update for method update_attributes. It''s worth noting that Officer extends off of Person, which has it''s own validations to do. I have several questions about this. First, which function should I define for validating create and update? What are the pros and cons on each one? I''ve also noticed that I cannot use validate_length_of and other helper methods inside these, and wondered why I''m not allowed to. Does this mean I have to make my own validation? If I do have to make my own validations within these methods, I will have to verify that the pseudo-parameters :password and :password_confirmation exists. Do I have to call attr_accessor on either :password, :password_confirmation, or both in this case? Many thanks in advance! P.S. Currently, my Officer and Person model looks like this: ----- class Officer < Person #take care of password thingy validates_length_of :password, :within => 5..20 #attr_accessor :password_confirmation validates_presence_of :password_confirmation, :if => :password validates_confirmation_of :password #some functions def validate errors.add_to_base("Missing password") if hashed_password.blank? end def self.authenticate( rin, password ) person = self.find( :first, :conditions => [ ''rin = ?'', rin ] ) if person expected_password = encrypted_password( password, person.salt ) if person.hashed_password != expected_password person = nil end end person end end ----- class Person < ActiveRecord::Base #first_name is required: must start with a capital validates_presence_of :first_name validates_format_of :first_name, :with => /^[A-Z][a-zA-Z0-9, .]+$/ #last_name is required: must start with a capital validates_presence_of :last_name validates_format_of :last_name, :with => /^[A-Z][a-zA-Z0-9, .]+$/ #rin is required: must be unique; also, must all be lowercases #with 0 to 2 numbers following the letters. validates_presence_of :rin, :message => '': A Rensselaer ID must be provided'' validates_format_of :rin, :with => /^[a-z]+[0-9]{0,2}$/, :message => '': Invalid Rensselaer ID'' validates_uniqueness_of :rin, :message => '': Someone already has this Rensselaer ID'' #email must be unique, and it is required. validates_presence_of :email validates_format_of :email, :with => /^[a-zA-Z0-9_]+@[a-zA-Z0-9_]+(\.[a-z]{2,3}){1,2}$/ validates_uniqueness_of :email #year is required, and must be a 4 digit number greater than 2000 validates_presence_of :year validates_numericality_of :year, :only_integer => true validates_length_of :year, :is => 4 #completely ignore the passwords def password @password end def password=(pwd) @password = pwd create_new_salt self.hashed_password = Officer.encrypted_password(self.password, self.salt) end protected def validate errors.add(:year, "should be between 2000 and 3000") if ( year.to_i < 2000 or year.to_i > 3000 ) end def self.encrypted_password(password, salt) string_to_hash = password + "Japan is an island of interest" + salt Digest::SHA1.hexdigest(string_to_hash) end def create_new_salt self.salt = (self.object_id * rand).to_s + rand.to_s end end --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I tried this: ------ class Officer < Person #take care of password thingy attr_accessor :password_confirmation #some functions I want to imitate for on_create at all times, #and on_update, only if password is not nil #def validate # errors.add_to_base("Missing password") if hashed_password.blank? #end #validates_presence_of :password_confirmation, :if => :password #validates_confirmation_of :password #validates_length_of :password, :within => 5..20 #functions that doesn''t work def before_validation_on_create if @password.nil? or @password.empty? errors.add_to_base ''You must provide a password'' elsif @password.size < 5 or @password.size > 20 errors.add_to_base ''Password must be within 5 and 20 characters'' end if @password_confirmation.nil? or @password_confirmation.empty? errors.add_to_base ''You must provide a password confirmation'' elsif @password_confirmation != @password errors.add_to_base ''Password does not match with Password Confirmation'' end end def before_validation_on_update unless @password.nil? if @password.empty? errors.add_to_base ''You must provide a password'' elsif @password.size < 5 or @password.size > 20 errors.add_to_base ''Password must be within 5 and 20 characters'' end if @password_confirmation.nil? or @password_confirmation.empty? errors.add_to_base ''You must provide a password confirmation'' elsif @password_confirmation != @password errors.add_to_base ''Password does not match with Password Confirmation'' end end end #and functions that does def self.authenticate( rin, password ) person = self.find( :first, :conditions => [ ''rin = ?'', rin ] ) if person expected_password = encrypted_password( password, person.salt ) if person.hashed_password != expected_password person = nil end end person end #turns an Officer to Admin def turn_to_admin self.update_attribute_with_validation_skipping(:type, ''Admin'') end #This does nothing def turn_to_officer nil end #turns an Officer or Admin def turn_to_person self.update_attribute_with_validation_skipping(:type, ''Person'') end end ------ (Person.rb remains the same) But according to my unit tests, this doesn''t pass: ------ def test_defective_new officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''forka'') assert !officer.valid? #line 67, <false> is not true assert !officer.errors.empty? end def test_empty_password officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011) assert !officer.valid? #line 12, <false> is not true assert officer.errors.invalid?(:password) end def test_empty_password_confirmation officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jojoj'', :password_confirmation => '''') assert !officer.valid? #line 21, <false> is not true assert officer.errors.invalid?(:password) end def test_password_invalid_confirmation officer = Officer.find(:first) assert officer officer[:password] = ''testing'' officer[:confirmation_password] = ''paco'' officer.reload assert !officer.save #line 49, <false> is not true end def test_password_too_short officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jo'', :password_confirmation => ''jo'') assert !officer.valid? #line 39, <false> is not true assert officer.errors.invalid?(:password) end def test_unequal_password_confirmation officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jojoj'', :password_confirmation => ''kkkkk'') assert !officer.valid? #line 30, <false> is not true assert officer.errors.invalid?(:password) end ----- Loaded suite test/unit/officer_test Started ...FFFFFF. Finished in 0.438 seconds. 1) Failure: test_defective_new(OfficerTest) [test/unit/officer_test.rb:67:in `test_defective_new'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 2) Failure: test_empty_password(OfficerTest) [test/unit/officer_test.rb:12:in `test_empty_password'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 3) Failure: test_empty_password_confirmation(OfficerTest) [test/unit/officer_test.rb:21:in `test_empty_password_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 4) Failure: test_password_invalid_confirmation(OfficerTest) [test/unit/officer_test.rb:49:in `test_password_invalid_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 5) Failure: test_password_too_short(OfficerTest) [test/unit/officer_test.rb:39:in `test_password_too_short'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 6) Failure: test_unequal_password_confirmation(OfficerTest) [test/unit/officer_test.rb:30:in `test_unequal_password_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 10 tests, 18 assertions, 6 failures, 0 errors --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I tried this: ------ class Officer < Person #take care of password thingy attr_accessor :password_confirmation #some functions I want to imitate for on_create at all times, #and on_update, only if password is not nil #def validate # errors.add_to_base("Missing password") if hashed_password.blank? #end #validates_presence_of :password_confirmation, :if => :password #validates_confirmation_of :password #validates_length_of :password, :within => 5..20 #functions that doesn''t work def before_validation_on_create if @password.nil? or @password.empty? errors.add_to_base ''You must provide a password'' elsif @password.size < 5 or @password.size > 20 errors.add_to_base ''Password must be within 5 and 20 characters'' end if @password_confirmation.nil? or @password_confirmation.empty? errors.add_to_base ''You must provide a password confirmation'' elsif @password_confirmation != @password errors.add_to_base ''Password does not match with Password Confirmation'' end end def before_validation_on_update unless @password.nil? if @password.empty? errors.add_to_base ''You must provide a password'' elsif @password.size < 5 or @password.size > 20 errors.add_to_base ''Password must be within 5 and 20 characters'' end if @password_confirmation.nil? or @password_confirmation.empty? errors.add_to_base ''You must provide a password confirmation'' elsif @password_confirmation != @password errors.add_to_base ''Password does not match with Password Confirmation'' end end end #and functions that does def self.authenticate( rin, password ) person = self.find( :first, :conditions => [ ''rin = ?'', rin ] ) if person expected_password = encrypted_password( password, person.salt ) if person.hashed_password != expected_password person = nil end end person end #turns an Officer to Admin def turn_to_admin self.update_attribute_with_validation_skipping(:type, ''Admin'') end #This does nothing def turn_to_officer nil end #turns an Officer or Admin def turn_to_person self.update_attribute_with_validation_skipping(:type, ''Person'') end end ------ (Person.rb remains the same) But according to my unit tests, this doesn''t pass: ------ def test_defective_new officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''forka'') assert !officer.valid? #line 67, <false> is not true assert !officer.errors.empty? end def test_empty_password officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011) assert !officer.valid? #line 12, <false> is not true assert officer.errors.invalid?(:password) end def test_empty_password_confirmation officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jojoj'', :password_confirmation => '''') assert !officer.valid? #line 21, <false> is not true assert officer.errors.invalid?(:password) end def test_password_invalid_confirmation officer = Officer.find(:first) assert officer officer[:password] = ''testing'' officer[:confirmation_password] = ''paco'' officer.reload assert !officer.save #line 49, <false> is not true end def test_password_too_short officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jo'', :password_confirmation => ''jo'') assert !officer.valid? #line 39, <false> is not true assert officer.errors.invalid?(:password) end def test_unequal_password_confirmation officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jojoj'', :password_confirmation => ''kkkkk'') assert !officer.valid? #line 30, <false> is not true assert officer.errors.invalid?(:password) end ----- Loaded suite test/unit/officer_test Started ...FFFFFF. Finished in 0.438 seconds. 1) Failure: test_defective_new(OfficerTest) [test/unit/officer_test.rb:67:in `test_defective_new'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 2) Failure: test_empty_password(OfficerTest) [test/unit/officer_test.rb:12:in `test_empty_password'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 3) Failure: test_empty_password_confirmation(OfficerTest) [test/unit/officer_test.rb:21:in `test_empty_password_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 4) Failure: test_password_invalid_confirmation(OfficerTest) [test/unit/officer_test.rb:49:in `test_password_invalid_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 5) Failure: test_password_too_short(OfficerTest) [test/unit/officer_test.rb:39:in `test_password_too_short'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 6) Failure: test_unequal_password_confirmation(OfficerTest) [test/unit/officer_test.rb:30:in `test_unequal_password_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 10 tests, 18 assertions, 6 failures, 0 errors --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I tried this: ------ class Officer < Person #take care of password thingy attr_accessor :password_confirmation #some functions I want to imitate for on_create at all times, #and on_update, only if password is not nil #def validate # errors.add_to_base("Missing password") if hashed_password.blank? #end #validates_presence_of :password_confirmation, :if => :password #validates_confirmation_of :password #validates_length_of :password, :within => 5..20 #functions that doesn''t work def before_validation_on_create if @password.nil? or @password.empty? errors.add_to_base ''You must provide a password'' elsif @password.size < 5 or @password.size > 20 errors.add_to_base ''Password must be within 5 and 20 characters'' end if @password_confirmation.nil? or @password_confirmation.empty? errors.add_to_base ''You must provide a password confirmation'' elsif @password_confirmation != @password errors.add_to_base ''Password does not match with Password Confirmation'' end end def before_validation_on_update unless @password.nil? if @password.empty? errors.add_to_base ''You must provide a password'' elsif @password.size < 5 or @password.size > 20 errors.add_to_base ''Password must be within 5 and 20 characters'' end if @password_confirmation.nil? or @password_confirmation.empty? errors.add_to_base ''You must provide a password confirmation'' elsif @password_confirmation != @password errors.add_to_base ''Password does not match with Password Confirmation'' end end end #and functions that does def self.authenticate( rin, password ) person = self.find( :first, :conditions => [ ''rin = ?'', rin ] ) if person expected_password = encrypted_password( password, person.salt ) if person.hashed_password != expected_password person = nil end end person end #turns an Officer to Admin def turn_to_admin self.update_attribute_with_validation_skipping(:type, ''Admin'') end #This does nothing def turn_to_officer nil end #turns an Officer or Admin def turn_to_person self.update_attribute_with_validation_skipping(:type, ''Person'') end end ------ (Person.rb remains the same) But according to my unit tests, this doesn''t pass: ------ def test_defective_new officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''forka'') assert !officer.valid? #line 67, <false> is not true assert !officer.errors.empty? end def test_empty_password officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011) assert !officer.valid? #line 12, <false> is not true assert officer.errors.invalid?(:password) end def test_empty_password_confirmation officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jojoj'', :password_confirmation => '''') assert !officer.valid? #line 21, <false> is not true assert officer.errors.invalid?(:password) end def test_password_invalid_confirmation officer = Officer.find(:first) assert officer officer[:password] = ''testing'' officer[:confirmation_password] = ''paco'' officer.reload assert !officer.save #line 49, <false> is not true end def test_password_too_short officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jo'', :password_confirmation => ''jo'') assert !officer.valid? #line 39, <false> is not true assert officer.errors.invalid?(:password) end def test_unequal_password_confirmation officer = Officer.new( :first_name => ''John'', :last_name => ''Doe'', :rin => ''doej'', :email => ''doej-IL7dBOYR4Vg@public.gmane.org'', :year => 2011, :password => ''jojoj'', :password_confirmation => ''kkkkk'') assert !officer.valid? #line 30, <false> is not true assert officer.errors.invalid?(:password) end ----- Loaded suite test/unit/officer_test Started ...FFFFFF. Finished in 0.438 seconds. 1) Failure: test_defective_new(OfficerTest) [test/unit/officer_test.rb:67:in `test_defective_new'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 2) Failure: test_empty_password(OfficerTest) [test/unit/officer_test.rb:12:in `test_empty_password'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 3) Failure: test_empty_password_confirmation(OfficerTest) [test/unit/officer_test.rb:21:in `test_empty_password_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 4) Failure: test_password_invalid_confirmation(OfficerTest) [test/unit/officer_test.rb:49:in `test_password_invalid_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 5) Failure: test_password_too_short(OfficerTest) [test/unit/officer_test.rb:39:in `test_password_too_short'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 6) Failure: test_unequal_password_confirmation(OfficerTest) [test/unit/officer_test.rb:30:in `test_unequal_password_confirmation'' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/ active_support/testing/default.rb:7:in `run'']: <false> is not true. 10 tests, 18 assertions, 6 failures, 0 errors --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Greg Willits
2008-Jun-15 19:02 UTC
Re: different password validation on create and on update
> On Jun 14, 2008, at 6:18 AM, Taro wrote: >> > For on create, I want to verify that the password and password > confirmation exists at all times. For update, want to make it > possible for providing password optional, and only when the password > is provided, to make sure it matches the password confirmation and the > within a certain lengthTaro, 3 tips for validations: (1) Have a look at these customized validation methods to help make validation code smaller and more readable for many scenarios: http://www.railsdev.ws/blog/11/custom-validations-in-rails/ (2) To validate a specific input only when it is not empty, use an :if => Proc.new technique. In my case, I use this for an email address like this: validates_as_email :userEmail, :if => Proc.new { |PrivilegedUser| PrivilegedUser.userEmail.length > 0 } where :userEmail is the HTML input, and PrivilegedUser is the name of the class. (3) For passwords, and other validations that require some custom logic, add a "validate" method to your class. ActiveRecord will call this automatically. Let''s assume your HTML inputs are pswd and verify_pswd: def validate # this disallows empty values if the record is new if self.new_record? && (@pswd.blank? || @verify_pswd.blank?) self.errors.add(:pswd, ''Password cannot be empty.'') end end There are several waysto slice this as there are a few validation callbacks, so have a look here to see which one best suits how you prefer to organize your logic: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html -- def gw writes_at ''www.railsdev.ws'' end --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
> def validate > > # this disallows empty values if the record is new > > if self.new_record? && (@pswd.blank? || @verify_pswd.blank?) > self.errors.add(:pswd, ''Password cannot be empty.'') > end > > endTHIS is what I was looking for. The new_record? method! Thanks you so much! By the way, I apologize for the triple post. They happened accidentally by the magic of F5 :-P. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---