├── Gemfile ├── lib ├── opal-activesupport.rb └── opal │ ├── activesupport │ └── version.rb │ └── activesupport.rb ├── opal ├── opal-activesupport.rb ├── active_support.rb └── active_support │ ├── core_ext │ ├── class.rb │ ├── hash.rb │ ├── kernel.rb │ ├── numeric │ │ ├── calculations.rb │ │ └── time.rb │ ├── object.rb │ ├── numeric.rb │ ├── array.rb │ ├── integer.rb │ ├── module.rb │ ├── kernel │ │ └── singleton_class.rb │ ├── string.rb │ ├── module │ │ ├── remove_method.rb │ │ ├── introspection.rb │ │ └── delegation.rb │ ├── object │ │ ├── json.rb │ │ ├── blank.rb │ │ └── try.rb │ ├── enumerable.rb │ ├── array │ │ ├── extract_options.rb │ │ ├── wrap.rb │ │ └── grouping.rb │ ├── hash │ │ └── indifferent_access.rb │ ├── integer │ │ └── time.rb │ ├── string │ │ ├── inflections.rb │ │ └── filters.rb │ └── class │ │ └── attribute.rb │ ├── time.rb │ ├── inflector.rb │ ├── core_ext.rb │ ├── hash_with_indifferent_access.rb │ ├── inflections.rb │ ├── inflector │ ├── inflections.rb │ └── methods.rb │ └── concern.rb ├── .travis.yml ├── test ├── empty_bool.rb ├── core_ext │ ├── module │ │ └── remove_method_test.rb │ ├── blank_test.rb │ ├── class │ │ └── attribute_test.rb │ ├── kernel_test.rb │ ├── object_and_class_ext_test.rb │ ├── module_test.rb │ ├── array_ext_test.rb │ ├── numeric_ext_test.rb │ └── string_ext_test.rb ├── minitest │ └── autorun.rb ├── abstract_unit.rb ├── concern_test.rb ├── constantize_test_cases.rb ├── inflector_test_cases.rb └── inflector_test.rb ├── .gitignore ├── Rakefile ├── LICENSE.txt ├── opal-activesupport.gemspec ├── README.md └── CHANGELOG.md /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /lib/opal-activesupport.rb: -------------------------------------------------------------------------------- 1 | require 'opal/activesupport' 2 | -------------------------------------------------------------------------------- /opal/opal-activesupport.rb: -------------------------------------------------------------------------------- 1 | require 'active_support' 2 | -------------------------------------------------------------------------------- /opal/active_support.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext' 2 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/class.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/class/attribute' 2 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/hash.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/hash/indifferent_access' 2 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/kernel.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/kernel/singleton_class' 2 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/numeric/calculations.rb: -------------------------------------------------------------------------------- 1 | class << Time 2 | alias :current :now 3 | end 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | rvm: 2.6.2 4 | 5 | sudo: false 6 | 7 | cache: 8 | bundler: true 9 | 10 | -------------------------------------------------------------------------------- /lib/opal/activesupport/version.rb: -------------------------------------------------------------------------------- 1 | module Opal 2 | module Activesupport 3 | VERSION = '0.3.3' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/object.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/object/blank' 2 | require 'active_support/core_ext/object/try' 3 | -------------------------------------------------------------------------------- /test/empty_bool.rb: -------------------------------------------------------------------------------- 1 | class EmptyTrue 2 | def empty?() true; end 3 | end 4 | 5 | class EmptyFalse 6 | def empty?() false; end 7 | end 8 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/numeric.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/numeric/time' 2 | require 'active_support/core_ext/numeric/calculations' 3 | -------------------------------------------------------------------------------- /opal/active_support/time.rb: -------------------------------------------------------------------------------- 1 | require 'date' 2 | require 'time' 3 | require 'active_support/core_ext/integer/time' 4 | require 'active_support/core_ext/numeric/time' 5 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/array.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/array/extract_options' 2 | require 'active_support/core_ext/array/wrap' 3 | require 'active_support/core_ext/array/grouping' 4 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/integer.rb: -------------------------------------------------------------------------------- 1 | # require 'active_support/core_ext/integer/multiple' 2 | # require 'active_support/core_ext/integer/inflections' 3 | require 'active_support/core_ext/integer/time' 4 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/module.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/module/introspection' 2 | require 'active_support/core_ext/module/remove_method' 3 | require 'active_support/core_ext/module/delegation' 4 | -------------------------------------------------------------------------------- /lib/opal/activesupport.rb: -------------------------------------------------------------------------------- 1 | require 'opal' 2 | require 'opal/activesupport/version' 3 | 4 | # Just register our opal code path with opal build tools 5 | Opal.append_path File.expand_path('../../../opal', __FILE__) 6 | -------------------------------------------------------------------------------- /opal/active_support/inflector.rb: -------------------------------------------------------------------------------- 1 | require "active_support/inflector/inflections" 2 | require "active_support/inflector/methods" 3 | 4 | require "active_support/inflections" 5 | require "active_support/core_ext/string/inflections" 6 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/kernel/singleton_class.rb: -------------------------------------------------------------------------------- 1 | module Kernel 2 | # class_eval on an object acts like singleton_class.class_eval. 3 | def class_eval(*args, &block) 4 | singleton_class.class_eval(*args, &block) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/string.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/string/filters' 2 | require 'active_support/core_ext/string/inflections' 3 | 4 | class String 5 | def parameterize 6 | self.downcase.strip.gsub(/\W+/, '-') 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | /original-activesupport 19 | -------------------------------------------------------------------------------- /opal/active_support/core_ext.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/array' 2 | require 'active_support/core_ext/class' 3 | require 'active_support/core_ext/enumerable' 4 | require 'active_support/core_ext/hash' 5 | require 'active_support/core_ext/integer' 6 | require 'active_support/core_ext/kernel' 7 | require 'active_support/core_ext/module' 8 | require 'active_support/core_ext/numeric' 9 | require 'active_support/core_ext/object' 10 | require 'active_support/core_ext/string' 11 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/module/remove_method.rb: -------------------------------------------------------------------------------- 1 | class Module 2 | # Removes the named method, if it exists. 3 | def remove_possible_method(method) 4 | if method_defined?(method) || private_method_defined?(method) 5 | undef_method(method) 6 | end 7 | end 8 | 9 | # Replaces the existing method definition, if there is one, with the passed 10 | # block as its body. 11 | def redefine_method(method, &block) 12 | remove_possible_method(method) 13 | define_method(method, &block) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/object/json.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | class Object 4 | def as_json 5 | nil 6 | end 7 | end 8 | 9 | class Boolean 10 | def as_json 11 | self 12 | end 13 | end 14 | 15 | class NilClass 16 | def as_json 17 | self 18 | end 19 | end 20 | 21 | class Numeric 22 | def as_json 23 | self 24 | end 25 | end 26 | 27 | class String 28 | def as_json 29 | self 30 | end 31 | end 32 | 33 | class Array 34 | def as_json(options = nil) #:nodoc: 35 | map { |v| options ? v.as_json(options.dup) : v.as_json } 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/enumerable.rb: -------------------------------------------------------------------------------- 1 | module Enumerable 2 | def index_by(&block) 3 | return enum_for :index_by unless block_given? 4 | 5 | hash = Hash.new 6 | 7 | %x{ 8 | var result; 9 | 10 | self.$each._p = function() { 11 | var param = #{Opal.destructure(`arguments`)}, 12 | value = $opal.$yield1(block, param); 13 | 14 | if (value === $breaker) { 15 | result = $breaker.$v; 16 | return $breaker; 17 | } 18 | 19 | #{hash[`value`] = `param`}; 20 | } 21 | 22 | self.$each(); 23 | 24 | if (result !== undefined) { 25 | return result; 26 | } 27 | } 28 | 29 | hash 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /test/core_ext/module/remove_method_test.rb: -------------------------------------------------------------------------------- 1 | require 'abstract_unit' 2 | require 'active_support/core_ext/module/remove_method' 3 | 4 | module RemoveMethodTests 5 | class A 6 | def do_something 7 | return 1 8 | end 9 | 10 | end 11 | end 12 | 13 | class RemoveMethodTest < ActiveSupport::TestCase 14 | 15 | def test_remove_method_from_an_object 16 | RemoveMethodTests::A.class_eval{ 17 | self.remove_possible_method(:do_something) 18 | } 19 | assert !RemoveMethodTests::A.new.respond_to?(:do_something) 20 | end 21 | 22 | def test_redefine_method_in_an_object 23 | RemoveMethodTests::A.class_eval{ 24 | self.redefine_method(:do_something) { return 100 } 25 | } 26 | assert_equal 100, RemoveMethodTests::A.new.do_something 27 | end 28 | 29 | end -------------------------------------------------------------------------------- /test/minitest/autorun.rb: -------------------------------------------------------------------------------- 1 | require 'minitest' 2 | 3 | def Minitest.run args = [] 4 | # PORT: removed 5 | #self.load_plugins 6 | 7 | options = process_args args 8 | 9 | reporter = Minitest::CompositeReporter.new 10 | reporter << Minitest::SummaryReporter.new(options[:io], options) 11 | reporter << Minitest::ProgressReporter.new(options[:io], options) 12 | 13 | self.reporter = reporter # this makes it available to plugins 14 | # PORT: removed 15 | #self.init_plugins options 16 | self.reporter = nil # runnables shouldn't depend on the reporter, ever 17 | 18 | reporter.start 19 | __run reporter, options 20 | # PORT: removed 21 | #self.parallel_executor.shutdown 22 | reporter.report 23 | 24 | # PORT: modified 25 | reporter.passed? 26 | # `window.OPAL_TEST_EXIT_STATUS = #{reporter.passed?}` 27 | end 28 | 29 | at_exit { exit Minitest.run } 30 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler.require 3 | require 'bundler/gem_tasks' 4 | 5 | 6 | task :test do 7 | require 'opal' 8 | require 'opal/cli_runners' 9 | require 'opal/minitest' 10 | 11 | Opal::Config.arity_check_enabled = true 12 | Opal::Config.dynamic_require_severity = :warning 13 | 14 | Opal.append_path 'opal' 15 | Opal.append_path 'test' 16 | 17 | builder = Opal::Builder.new 18 | builder.build 'opal' 19 | builder.build 'opal/platform' 20 | builder.build 'minitest' 21 | Dir['test/**/*_test.rb'].map do |file| 22 | builder.build file.sub(%r{^test/}, '') 23 | end 24 | builder.build_str 'Minitest.run', 'minitest-runner.rb' 25 | 26 | runner_name = ENV['RUNNER'] || 'nodejs' 27 | runner_class = Opal::CliRunners.const_get(runner_name.capitalize) 28 | runner_class.new(output: $stdout).run(builder.to_s, []) 29 | end 30 | 31 | task default: :test 32 | -------------------------------------------------------------------------------- /opal/active_support/hash_with_indifferent_access.rb: -------------------------------------------------------------------------------- 1 | module ActiveSupport 2 | class HashWithIndifferentAccess < Hash 3 | # Returns +true+ so that Array#extract_options! finds members of 4 | # this class. 5 | def extractable_options? 6 | true 7 | end 8 | 9 | def with_indifferent_access 10 | dup 11 | end 12 | 13 | def nested_under_indifferent_access 14 | self 15 | end 16 | 17 | def initialize(constructor = {}) 18 | if constructor.respond_to?(:to_hash) 19 | super() 20 | update(constructor) 21 | else 22 | super(constructor) 23 | end 24 | end 25 | 26 | def self.new_from_hash_copying_default(hash) 27 | hash = hash.to_hash 28 | new(hash).tap do |new_hash| 29 | new_hash.default = hash.default 30 | new_hash.default_proc = hash.default_proc if hash.default_proc 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/array/extract_options.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | # By default, only instances of Hash itself are extractable. 3 | # Subclasses of Hash may implement this method and return 4 | # true to declare themselves as extractable. If a Hash 5 | # is extractable, Array#extract_options! pops it from 6 | # the Array when it is the last element of the Array. 7 | def extractable_options? 8 | instance_of?(Hash) 9 | end 10 | end 11 | 12 | class Array 13 | # Extracts options from a set of arguments. Removes and returns the last 14 | # element in the array if it's a hash, otherwise returns a blank hash. 15 | # 16 | # def options(*args) 17 | # args.extract_options! 18 | # end 19 | # 20 | # options(1, 2) # => {} 21 | # options(1, 2, a: :b) # => {:a=>:b} 22 | def extract_options! 23 | if last.is_a?(Hash) && last.extractable_options? 24 | pop 25 | else 26 | {} 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/core_ext/blank_test.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'abstract_unit' 4 | require 'active_support/core_ext/object/blank' 5 | 6 | class BlankTest < ActiveSupport::TestCase 7 | BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", ' ', [], {} ] 8 | NOT = [ EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], { nil => 0 } ] 9 | 10 | def test_blank 11 | BLANK.each { |v| assert v.blank?, "#{v.inspect} should be blank" } 12 | NOT.each { |v| assert !v.blank?, "#{v.inspect} should not be blank" } 13 | end 14 | 15 | def test_present 16 | BLANK.each { |v| assert !v.present?, "#{v.inspect} should not be present" } 17 | NOT.each { |v| assert v.present?, "#{v.inspect} should be present" } 18 | end 19 | 20 | def test_presence 21 | BLANK.each { |v| assert_equal nil, v.presence, "#{v.inspect}.presence should return nil" } 22 | NOT.each { |v| assert_equal v, v.presence, "#{v.inspect}.presence should return self" } 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/hash/indifferent_access.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/hash_with_indifferent_access' 2 | 3 | class Hash 4 | 5 | # Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver: 6 | # 7 | # { a: 1 }.with_indifferent_access['a'] # => 1 8 | def with_indifferent_access 9 | ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self) 10 | end 11 | 12 | # Called when object is nested under an object that receives 13 | # #with_indifferent_access. This method will be called on the current object 14 | # by the enclosing object and is aliased to #with_indifferent_access by 15 | # default. Subclasses of Hash may overwrite this method to return +self+ if 16 | # converting to an ActiveSupport::HashWithIndifferentAccess would not be 17 | # desirable. 18 | # 19 | # b = { b: 1 } 20 | # { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access 21 | # # => {"b"=>1} 22 | alias nested_under_indifferent_access with_indifferent_access 23 | end 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Elia Schito 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /opal-activesupport.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'opal/activesupport/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = 'opal-activesupport' 8 | gem.version = Opal::Activesupport::VERSION 9 | gem.authors = ['Elia Schito'] 10 | gem.email = ['elia@schito.me'] 11 | gem.description = %q{The port of the glorious ActiveSupport for Opal} 12 | gem.summary = %q{The port of the glorious ActiveSupport for Opal} 13 | gem.homepage = 'http://opalrb.org' 14 | gem.rdoc_options << '--main' << 'README' << 15 | '--line-numbers' << 16 | '--include' << 'opal' 17 | 18 | gem.files = `git ls-files`.split($/) 19 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 20 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 21 | gem.require_paths = ['lib'] 22 | 23 | gem.add_dependency 'opal', ['>= 0.5.0', '< 2'] 24 | gem.add_development_dependency 'opal-minitest' 25 | gem.add_development_dependency 'opal-sprockets' 26 | gem.add_development_dependency 'rake' 27 | end 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Opal: ActiveSupport 2 | 3 | [![Build Status](https://travis-ci.org/opal/opal-activesupport.svg?branch=master)](https://travis-ci.org/opal/opal-activesupport) 4 | 5 | > @AstonJ But it's vanilla Ruby. It's not like you have ActiveSupport available, which somewhat defeats it for me. 6 | 7 | _[@dhh 6:44 PM - Oct 23, 2012](https://twitter.com/dhh/status/260783823254601728)_ 8 | 9 | 10 | ## Installation 11 | 12 | Add this line to your application's Gemfile: 13 | 14 | gem 'opal-activesupport' 15 | 16 | And then execute: 17 | 18 | $ bundle 19 | 20 | Or install it yourself as: 21 | 22 | $ gem install opal-activesupport 23 | 24 | 25 | ## Usage 26 | 27 | Inside your `application.js.rb`: 28 | 29 | ```ruby 30 | require 'active_support' # to require the whole active support lib 31 | require 'active_support/core_ext' # require only the corelib extensions 32 | require 'active_support/core_ext/string' # require only the corelib extensions 33 | ``` 34 | 35 | 36 | ## Contributing 37 | 38 | 1. Fork it 39 | 2. Create your feature branch (`git checkout -b my-new-feature`) 40 | 3. Commit your changes (`git commit -am 'Add some feature'`) 41 | 4. Push to the branch (`git push origin my-new-feature`) 42 | 5. Create new Pull Request 43 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/module/introspection.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/inflector' 2 | 3 | class Module 4 | # Returns the name of the module containing this one. 5 | # 6 | # M::N.parent_name # => "M" 7 | def parent_name 8 | if defined? @parent_name 9 | @parent_name 10 | else 11 | @parent_name = name =~ /::[^:]+$/ ? $`.freeze : nil 12 | end 13 | end 14 | 15 | # Returns the module which contains this one according to its name. 16 | # 17 | # module M 18 | # module N 19 | # end 20 | # end 21 | # X = M::N 22 | # 23 | # M::N.parent # => M 24 | # X.parent # => M 25 | # 26 | # The parent of top-level and anonymous modules is Object. 27 | # 28 | # M.parent # => Object 29 | # Module.new.parent # => Object 30 | def parent 31 | parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object 32 | end 33 | 34 | # Returns all the parents of this module according to its name, ordered from 35 | # nested outwards. The receiver is not contained within the result. 36 | # 37 | # module M 38 | # module N 39 | # end 40 | # end 41 | # X = M::N 42 | # 43 | # M.parents # => [Object] 44 | # M::N.parents # => [M, Object] 45 | # X.parents # => [M, Object] 46 | def parents 47 | parents = [] 48 | if parent_name 49 | parts = parent_name.split('::') 50 | until parts.empty? 51 | parents << ActiveSupport::Inflector.constantize(parts * '::') 52 | parts.pop 53 | end 54 | end 55 | parents << Object unless parents.include? Object 56 | parents 57 | end 58 | 59 | def local_constants #:nodoc: 60 | constants(false) 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/integer/time.rb: -------------------------------------------------------------------------------- 1 | # require 'active_support/duration' 2 | require 'active_support/core_ext/numeric/time' 3 | 4 | # class Integer 5 | class Numeric 6 | # Enables the use of time calculations and declarations, like 45.minutes + 7 | # 2.hours + 4.years. 8 | # 9 | # These methods use Time#advance for precise date calculations when using 10 | # from_now, +ago+, etc. as well as adding or subtracting their 11 | # results from a Time object. 12 | # 13 | # # equivalent to Time.now.advance(months: 1) 14 | # 1.month.from_now 15 | # 16 | # # equivalent to Time.now.advance(years: 2) 17 | # 2.years.from_now 18 | # 19 | # # equivalent to Time.now.advance(months: 4, years: 5) 20 | # (4.months + 5.years).from_now 21 | # 22 | # While these methods provide precise calculation when used as in the examples 23 | # above, care should be taken to note that this is not true if the result of 24 | # +months+, +years+, etc is converted before use: 25 | # 26 | # # equivalent to 30.days.to_i.from_now 27 | # 1.month.to_i.from_now 28 | # 29 | # # equivalent to 365.25.days.to_f.from_now 30 | # 1.year.to_f.from_now 31 | # 32 | # In such cases, Ruby's core 33 | # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and 34 | # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision 35 | # date and time arithmetic. 36 | def months 37 | # ActiveSupport::Duration.new(self * 30.days, [[:months, self]]) 38 | self * 30.days 39 | end 40 | alias :month :months 41 | 42 | def years 43 | # ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]]) 44 | self * 365.25.days 45 | end 46 | alias :year :years 47 | end 48 | -------------------------------------------------------------------------------- /test/abstract_unit.rb: -------------------------------------------------------------------------------- 1 | # ORIG_ARGV = ARGV.dup 2 | # 3 | # begin 4 | # old, $VERBOSE = $VERBOSE, nil 5 | # require File.expand_path('../../../load_paths', __FILE__) 6 | # ensure 7 | # $VERBOSE = old 8 | # end 9 | # 10 | # require 'active_support/core_ext/kernel/reporting' 11 | # require 'active_support/core_ext/string/encoding' 12 | # 13 | # silence_warnings do 14 | # Encoding.default_internal = "UTF-8" 15 | # Encoding.default_external = "UTF-8" 16 | # end 17 | # 18 | # require 'active_support/testing/autorun' 19 | # require 'empty_bool' 20 | # 21 | # ENV['NO_RELOAD'] = '1' 22 | # require 'active_support' 23 | # 24 | # Thread.abort_on_exception = true 25 | # 26 | # # Show backtraces for deprecated behavior for quicker cleanup. 27 | # ActiveSupport::Deprecation.debug = true 28 | 29 | # To help opal-minitest print errors. 30 | Encoding.default_external = Encoding::UTF_8 31 | 32 | require 'minitest/autorun' 33 | require 'empty_bool' 34 | require 'active_support' 35 | 36 | class ActiveSupport::TestCase < Minitest::Test 37 | def self.test name, &block 38 | define_method "test_#{name.gsub(/[\W-]+/, '_')}", &block 39 | end 40 | 41 | def assert_raise error_class, &block 42 | block.call 43 | assert false, "Expected to see #{error_class.inspect}, but no exception was raised" 44 | rescue error_class => error 45 | error 46 | rescue => actual_error 47 | assert false, "Expected to see #{error_class.inspect}, but got #{actual_error.inspect}" 48 | end 49 | 50 | def assert_nothing_raised(&block) 51 | block.call 52 | rescue => e 53 | assert false, "Expected no error, but got #{e.inspect}" 54 | end 55 | 56 | def assert_not_empty(object) 57 | assert !object.empty?, "Expected not empty object, but got: #{object.inspect}" 58 | end 59 | 60 | def assert_not_same(obj1, obj2) 61 | assert !obj1.equal?(obj2), "Excepted #{obj1.inspect} and #{obj2.inspect} to be different objects" 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/array/wrap.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | # Wraps its argument in an array unless it is already an array (or array-like). 3 | # 4 | # Specifically: 5 | # 6 | # * If the argument is +nil+ an empty list is returned. 7 | # * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned. 8 | # * Otherwise, returns an array with the argument as its single element. 9 | # 10 | # Array.wrap(nil) # => [] 11 | # Array.wrap([1, 2, 3]) # => [1, 2, 3] 12 | # Array.wrap(0) # => [0] 13 | # 14 | # This method is similar in purpose to Kernel#Array, but there are some differences: 15 | # 16 | # * If the argument responds to +to_ary+ the method is invoked. Kernel#Array 17 | # moves on to try +to_a+ if the returned value is +nil+, but Array.wrap returns 18 | # such a +nil+ right away. 19 | # * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, Kernel#Array 20 | # raises an exception, while Array.wrap does not, it just returns the value. 21 | # * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array. 22 | # 23 | # The last point is particularly worth comparing for some enumerables: 24 | # 25 | # Array(foo: :bar) # => [[:foo, :bar]] 26 | # Array.wrap(foo: :bar) # => [{:foo=>:bar}] 27 | # 28 | # There's also a related idiom that uses the splat operator: 29 | # 30 | # [*object] 31 | # 32 | # which for +nil+ returns [], and calls to Array(object) otherwise. 33 | # 34 | # Thus, in this case the behavior may be different for +nil+, and the differences with 35 | # Kernel#Array explained above apply to the rest of objects. 36 | def self.wrap(object) 37 | if object.nil? 38 | [] 39 | elsif object.respond_to?(:to_ary) 40 | object.to_ary || [object] 41 | else 42 | [object] 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/string/inflections.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/inflector' 2 | 3 | class String 4 | def pluralize(count = nil, locale = :en) 5 | locale = count if count.is_a?(Symbol) 6 | if count == 1 7 | dup 8 | else 9 | ActiveSupport::Inflector.pluralize(self, locale) 10 | end 11 | end 12 | 13 | def singularize(locale = :en) 14 | ActiveSupport::Inflector.singularize(self, locale) 15 | end 16 | 17 | def constantize 18 | ActiveSupport::Inflector.constantize(self) 19 | end 20 | 21 | def safe_constantize 22 | ActiveSupport::Inflector.safe_constantize(self) 23 | end 24 | 25 | def camelize(first_letter = :upper) 26 | case first_letter 27 | when :upper 28 | ActiveSupport::Inflector.camelize(self, true) 29 | when :lower 30 | ActiveSupport::Inflector.camelize(self, false) 31 | else 32 | raise ArgumentError, "Invalid option, use either :upper or :lower." 33 | end 34 | end 35 | alias_method :camelcase, :camelize 36 | 37 | def titleize(keep_id_suffix: false) 38 | ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix) 39 | end 40 | alias_method :titlecase, :titleize 41 | 42 | def underscore 43 | ActiveSupport::Inflector.underscore(self) 44 | end 45 | 46 | def dasherize 47 | ActiveSupport::Inflector.dasherize(self) 48 | end 49 | 50 | def demodulize 51 | ActiveSupport::Inflector.demodulize(self) 52 | end 53 | 54 | def deconstantize 55 | ActiveSupport::Inflector.deconstantize(self) 56 | end 57 | 58 | def tableize 59 | ActiveSupport::Inflector.tableize(self) 60 | end 61 | 62 | def classify 63 | ActiveSupport::Inflector.classify(self) 64 | end 65 | 66 | def humanize(capitalize: true, keep_id_suffix: false) 67 | ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix) 68 | end 69 | 70 | def upcase_first 71 | ActiveSupport::Inflector.upcase_first(self) 72 | end 73 | 74 | def foreign_key(separate_class_name_and_id_with_underscore = true) 75 | ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore) 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.3.4 - 2019-07-14 2 | 3 | [Changes](https://github.com/opal/opal-activesupport/compare/v0.3.3...HEAD) 4 | 5 | ### 0.3.3 - 2019-07-14 6 | 7 | [Changes](https://github.com/opal/opal-activesupport/compare/v0.3.2...v0.3.3) 8 | 9 | - Add support for Opal v1.0 10 | 11 | ### 0.3.2 - 2019-04-27 12 | 13 | [Changes](https://github.com/opal/opal-activesupport/compare/v0.3.1...v0.3.2) 14 | 15 | - Added `ActiveSupport::Concern` (#19). 16 | - Added `String#truncate` (#22). 17 | - Properly backported: (#21). 18 | + `ActiveSupport::Inflector.pluralize` and `String#pluralize` 19 | + `ActiveSupport::Inflector.singularize` and `String#singularize` 20 | + `ActiveSupport::Inflector.constantize` and `String#constantize` 21 | + `ActiveSupport::Inflector.safe_constantize` and `String#safe_constantize` 22 | + `ActiveSupport::Inflector.camelize` and `String#camelize` 23 | + `ActiveSupport::Inflector.titleize` and `String#titleize` 24 | + `ActiveSupport::Inflector.underscore` and `String#underscore` 25 | + `ActiveSupport::Inflector.dasherize` and `String#dasherize` 26 | + `ActiveSupport::Inflector.demodulize` and `String#demodulize` 27 | + `ActiveSupport::Inflector.deconstantize` and `String#deconstantize` 28 | + `ActiveSupport::Inflector.tableize` and `String#tableize` 29 | + `ActiveSupport::Inflector.classify` and `String#classify` 30 | + `ActiveSupport::Inflector.humanize` and `String#humanize` 31 | + `ActiveSupport::Inflector.upcase_first` and `String#upcase_first` 32 | + `ActiveSupport::Inflector.foreign_key` and `String#foreign_key` 33 | 34 | ### 0.3.1 - 2018-01-28 35 | 36 | [Changes](https://github.com/opal/opal-activesupport/compare/v0.3.0...v0.3.1) 37 | 38 | - Fix `Inflections.irregular` 39 | - Fix `Inflector.apply_inflections` 40 | - Fix `Inflector.inflections` 41 | - Fix `return` handling of x-string for Opal v0.11 42 | 43 | ### 0.3.0 - 2015-12-23 44 | 45 | [Changes](https://github.com/opal/opal-activesupport/compare/v0.2.0...v0.3.0) 46 | 47 | ### 0.2.0 - 2015-10-08 48 | 49 | [Changes](https://github.com/opal/opal-activesupport/compare/v0.1.0...v0.2.0) 50 | 51 | ### 0.1.0 - 2015-02-03 52 | 53 | _the fogs of the past 🌫_ 54 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/module/delegation.rb: -------------------------------------------------------------------------------- 1 | class Module 2 | class DelegationError < NoMethodError; 3 | end 4 | 5 | def delegate(*methods) 6 | options = methods.pop 7 | unless options.is_a?(Hash) && to = options[:to] 8 | raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).' 9 | end 10 | 11 | prefix, allow_nil = options.values_at(:prefix, :allow_nil) 12 | 13 | if prefix == true && to =~ /^[^a-z_]/ 14 | raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.' 15 | end 16 | 17 | method_prefix = if prefix 18 | "#{prefix == true ? to : prefix}_" 19 | else 20 | '' 21 | end 22 | 23 | to = to.to_s 24 | 25 | methods.each do |method| 26 | # Attribute writer methods only accept one argument. Makes sure []= 27 | # methods still accept two arguments. 28 | has_block = (method =~ /[^\]]=$/) ? false : true 29 | method_name = method_prefix + method 30 | # Avoiding the eval approach rails/active_support takes since it was brittle with making this work 31 | resolve_to = lambda do |scope| 32 | if to.start_with?('@') 33 | ivar_name = to[1..-1] 34 | # Had problems with instance_variable_get 35 | `#{scope}[#{ivar_name}]` 36 | else 37 | scope.__send__(to) 38 | end 39 | end 40 | exception = lambda do |scope| 41 | DelegationError.new("#{scope}#{method_name} delegated to #{to}.#{method} but #{to} is nil: #{scope.inspect}", method_name) 42 | end 43 | if has_block 44 | define_method(method_name) do |*args, &block| 45 | to_resolved = resolve_to[self] 46 | unless to_resolved 47 | next if allow_nil 48 | raise exception[self] 49 | end 50 | to_resolved.__send__(method, *args, &block) 51 | end 52 | else 53 | define_method(method_name) do |arg| 54 | to_resolved = resolve_to[self] 55 | unless to_resolved 56 | next if allow_nil 57 | raise exception[self] 58 | end 59 | to_resolved.__send__(method, arg) 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /test/core_ext/class/attribute_test.rb: -------------------------------------------------------------------------------- 1 | require 'abstract_unit' 2 | require 'active_support/core_ext/class/attribute' 3 | 4 | class ClassAttributeTest < ActiveSupport::TestCase 5 | def setup 6 | @klass = Class.new { class_attribute :setting } 7 | @sub = Class.new(@klass) 8 | end 9 | 10 | test 'defaults to nil' do 11 | assert_nil @klass.setting 12 | assert_nil @sub.setting 13 | end 14 | 15 | test 'inheritable' do 16 | @klass.setting = 1 17 | assert_equal 1, @sub.setting 18 | end 19 | 20 | test 'overridable' do 21 | @sub.setting = 1 22 | assert_nil @klass.setting 23 | 24 | @klass.setting = 2 25 | assert_equal 1, @sub.setting 26 | 27 | assert_equal 1, Class.new(@sub).setting 28 | end 29 | 30 | test 'query method' do 31 | assert_equal false, @klass.setting? 32 | @klass.setting = 1 33 | assert_equal true, @klass.setting? 34 | end 35 | 36 | test 'instance reader delegates to class' do 37 | assert_nil @klass.new.setting 38 | 39 | @klass.setting = 1 40 | assert_equal 1, @klass.new.setting 41 | end 42 | 43 | test 'instance override' do 44 | object = @klass.new 45 | object.setting = 1 46 | assert_nil @klass.setting 47 | @klass.setting = 2 48 | assert_equal 1, object.setting 49 | end 50 | 51 | test 'instance query' do 52 | object = @klass.new 53 | assert_equal false, object.setting? 54 | object.setting = 1 55 | assert_equal true, object.setting? 56 | end 57 | 58 | test 'disabling instance writer' do 59 | object = Class.new { class_attribute :setting, :instance_writer => false }.new 60 | assert_raise(NoMethodError) { object.setting = 'boom' } 61 | end 62 | 63 | test 'disabling instance reader' do 64 | object = Class.new { class_attribute :setting, :instance_reader => false }.new 65 | assert_raise(NoMethodError) { object.setting } 66 | assert_raise(NoMethodError) { object.setting? } 67 | end 68 | 69 | test 'disabling both instance writer and reader' do 70 | object = Class.new { class_attribute :setting, :instance_accessor => false }.new 71 | assert_raise(NoMethodError) { object.setting } 72 | assert_raise(NoMethodError) { object.setting? } 73 | assert_raise(NoMethodError) { object.setting = 'boom' } 74 | end 75 | 76 | test 'works well with singleton classes' do 77 | object = @klass.new 78 | object.singleton_class.setting = 'foo' 79 | assert_equal 'foo', object.setting 80 | end 81 | 82 | test 'setter returns set value' do 83 | val = @klass.send(:setting=, 1) 84 | assert_equal 1, val 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/object/blank.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class Object 4 | # An object is blank if it's false, empty, or a whitespace string. 5 | # For example, '', ' ', +nil+, [], and {} are all blank. 6 | # 7 | # This simplifies: 8 | # 9 | # if address.nil? || address.empty? 10 | # 11 | # ...to: 12 | # 13 | # if address.blank? 14 | def blank? 15 | respond_to?(:empty?) ? empty? : !self 16 | end 17 | 18 | # An object is present if it's not blank?. 19 | def present? 20 | !blank? 21 | end 22 | 23 | # Returns object if it's present? otherwise returns +nil+. 24 | # object.presence is equivalent to object.present? ? object : nil. 25 | # 26 | # This is handy for any representation of objects where blank is the same 27 | # as not present at all. For example, this simplifies a common check for 28 | # HTTP POST/query parameters: 29 | # 30 | # state = params[:state] if params[:state].present? 31 | # country = params[:country] if params[:country].present? 32 | # region = state || country || 'US' 33 | # 34 | # ...becomes: 35 | # 36 | # region = params[:state].presence || params[:country].presence || 'US' 37 | def presence 38 | self if present? 39 | end 40 | end 41 | 42 | class NilClass 43 | # +nil+ is blank: 44 | # 45 | # nil.blank? # => true 46 | def blank? 47 | true 48 | end 49 | end 50 | 51 | class Boolean 52 | # +false+ is blank: 53 | # 54 | # false.blank? # => true 55 | # 56 | # +true+ is not blank: 57 | # 58 | # true.blank? # => false 59 | def blank? 60 | self == false 61 | end 62 | end 63 | 64 | class Array 65 | # An array is blank if it's empty: 66 | # 67 | # [].blank? # => true 68 | # [1,2,3].blank? # => false 69 | alias_method :blank?, :empty? 70 | end 71 | 72 | class Hash 73 | # A hash is blank if it's empty: 74 | # 75 | # {}.blank? # => true 76 | # { key: 'value' }.blank? # => false 77 | alias_method :blank?, :empty? 78 | end 79 | 80 | class String 81 | # A string is blank if it's empty or contains whitespaces only: 82 | # 83 | # ''.blank? # => true 84 | # ' '.blank? # => true 85 | # ' '.blank? # => true 86 | # ' something here '.blank? # => false 87 | def blank? 88 | self !~ /[^\s ]/ 89 | end 90 | end 91 | 92 | class Numeric #:nodoc: 93 | # No number is blank: 94 | # 95 | # 1.blank? # => false 96 | # 0.blank? # => false 97 | def blank? 98 | false 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/numeric/time.rb: -------------------------------------------------------------------------------- 1 | # require 'active_support/duration' 2 | # require 'active_support/core_ext/time/calculations' 3 | # require 'active_support/core_ext/time/acts_like' 4 | 5 | class Numeric 6 | # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. 7 | # 8 | # These methods use Time#advance for precise date calculations when using from_now, ago, etc. 9 | # as well as adding or subtracting their results from a Time object. For example: 10 | # 11 | # # equivalent to Time.current.advance(months: 1) 12 | # 1.month.from_now 13 | # 14 | # # equivalent to Time.current.advance(years: 2) 15 | # 2.years.from_now 16 | # 17 | # # equivalent to Time.current.advance(months: 4, years: 5) 18 | # (4.months + 5.years).from_now 19 | # 20 | # While these methods provide precise calculation when used as in the examples above, care 21 | # should be taken to note that this is not true if the result of `months', `years', etc is 22 | # converted before use: 23 | # 24 | # # equivalent to 30.days.to_i.from_now 25 | # 1.month.to_i.from_now 26 | # 27 | # # equivalent to 365.25.days.to_f.from_now 28 | # 1.year.to_f.from_now 29 | # 30 | # In such cases, Ruby's core 31 | # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and 32 | # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision 33 | # date and time arithmetic. 34 | def seconds 35 | # ActiveSupport::Duration.new(self, [[:seconds, self]]) 36 | self 37 | end 38 | alias :second :seconds 39 | 40 | def minutes 41 | # ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]]) 42 | self * 60 43 | end 44 | alias :minute :minutes 45 | 46 | def hours 47 | # ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]]) 48 | self * 3600 49 | end 50 | alias :hour :hours 51 | 52 | def days 53 | # ActiveSupport::Duration.new(self * 24.hours, [[:days, self]]) 54 | self * 24.hours 55 | end 56 | alias :day :days 57 | 58 | def weeks 59 | # ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]]) 60 | self * 7.days 61 | end 62 | alias :week :weeks 63 | 64 | def fortnights 65 | # ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]]) 66 | self * 2.weeks 67 | end 68 | alias :fortnight :fortnights 69 | 70 | # Reads best without arguments: 10.minutes.ago 71 | def ago(time = ::Time.current) 72 | time - self 73 | end 74 | 75 | # Reads best with argument: 10.minutes.until(time) 76 | alias :until :ago 77 | 78 | # Reads best with argument: 10.minutes.since(time) 79 | def since(time = ::Time.current) 80 | time + self 81 | end 82 | 83 | # Reads best without arguments: 10.minutes.from_now 84 | alias :from_now :since 85 | end 86 | -------------------------------------------------------------------------------- /opal/active_support/inflections.rb: -------------------------------------------------------------------------------- 1 | require "active_support/inflector/inflections" 2 | 3 | module ActiveSupport 4 | Inflector.inflections(:en) do |inflect| 5 | inflect.plural(/$/, 's') 6 | inflect.plural(/s$/i, 's') 7 | inflect.plural(/^(ax|test)is$/i, '\1es') 8 | inflect.plural(/(octop|vir)us$/i, '\1i') 9 | inflect.plural(/(octop|vir)i$/i, '\1i') 10 | inflect.plural(/(alias|status)$/i, '\1es') 11 | inflect.plural(/(bu)s$/i, '\1ses') 12 | inflect.plural(/(buffal|tomat)o$/i, '\1oes') 13 | inflect.plural(/([ti])um$/i, '\1a') 14 | inflect.plural(/([ti])a$/i, '\1a') 15 | inflect.plural(/sis$/i, 'ses') 16 | inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves') 17 | inflect.plural(/(hive)$/i, '\1s') 18 | inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies') 19 | inflect.plural(/(x|ch|ss|sh)$/i, '\1es') 20 | inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices') 21 | inflect.plural(/^(m|l)ouse$/i, '\1ice') 22 | inflect.plural(/^(m|l)ice$/i, '\1ice') 23 | inflect.plural(/^(ox)$/i, '\1en') 24 | inflect.plural(/^(oxen)$/i, '\1') 25 | inflect.plural(/(quiz)$/i, '\1zes') 26 | 27 | inflect.singular(/s$/i, '') 28 | inflect.singular(/(ss)$/i, '\1') 29 | inflect.singular(/(n)ews$/i, '\1ews') 30 | inflect.singular(/([ti])a$/i, '\1um') 31 | inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis') 32 | inflect.singular(/(^analy)(sis|ses)$/i, '\1sis') 33 | inflect.singular(/([^f])ves$/i, '\1fe') 34 | inflect.singular(/(hive)s$/i, '\1') 35 | inflect.singular(/(tive)s$/i, '\1') 36 | inflect.singular(/([lr])ves$/i, '\1f') 37 | inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y') 38 | inflect.singular(/(s)eries$/i, '\1eries') 39 | inflect.singular(/(m)ovies$/i, '\1ovie') 40 | inflect.singular(/(x|ch|ss|sh)es$/i, '\1') 41 | inflect.singular(/^(m|l)ice$/i, '\1ouse') 42 | inflect.singular(/(bus)(es)?$/i, '\1') 43 | inflect.singular(/(o)es$/i, '\1') 44 | inflect.singular(/(shoe)s$/i, '\1') 45 | inflect.singular(/(cris|test)(is|es)$/i, '\1is') 46 | inflect.singular(/^(a)x[ie]s$/i, '\1xis') 47 | inflect.singular(/(octop|vir)(us|i)$/i, '\1us') 48 | inflect.singular(/(alias|status)(es)?$/i, '\1') 49 | inflect.singular(/^(ox)en/i, '\1') 50 | inflect.singular(/(vert|ind)ices$/i, '\1ex') 51 | inflect.singular(/(matr)ices$/i, '\1ix') 52 | inflect.singular(/(quiz)zes$/i, '\1') 53 | inflect.singular(/(database)s$/i, '\1') 54 | 55 | inflect.irregular('person', 'people') 56 | inflect.irregular('man', 'men') 57 | inflect.irregular('child', 'children') 58 | inflect.irregular('sex', 'sexes') 59 | inflect.irregular('move', 'moves') 60 | inflect.irregular('zombie', 'zombies') 61 | 62 | inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police)) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/object/try.rb: -------------------------------------------------------------------------------- 1 | class Object 2 | # Invokes the public method whose name goes as first argument just like 3 | # +public_send+ does, except that if the receiver does not respond to it the 4 | # call returns +nil+ rather than raising an exception. 5 | # 6 | # This method is defined to be able to write 7 | # 8 | # @person.try(:name) 9 | # 10 | # instead of 11 | # 12 | # @person.name if @person 13 | # 14 | # +try+ calls can be chained: 15 | # 16 | # @person.try(:spouse).try(:name) 17 | # 18 | # instead of 19 | # 20 | # @person.spouse.name if @person && @person.spouse 21 | # 22 | # +try+ will also return +nil+ if the receiver does not respond to the method: 23 | # 24 | # @person.try(:non_existing_method) #=> nil 25 | # 26 | # instead of 27 | # 28 | # @person.non_existing_method if @person.respond_to?(:non_existing_method) #=> nil 29 | # 30 | # +try+ returns +nil+ when called on +nil+ regardless of whether it responds 31 | # to the method: 32 | # 33 | # nil.try(:to_i) # => nil, rather than 0 34 | # 35 | # Arguments and blocks are forwarded to the method if invoked: 36 | # 37 | # @posts.try(:each_slice, 2) do |a, b| 38 | # ... 39 | # end 40 | # 41 | # The number of arguments in the signature must match. If the object responds 42 | # to the method the call is attempted and +ArgumentError+ is still raised 43 | # in case of argument mismatch. 44 | # 45 | # If +try+ is called without arguments it yields the receiver to a given 46 | # block unless it is +nil+: 47 | # 48 | # @person.try do |p| 49 | # ... 50 | # end 51 | # 52 | # You can also call try with a block without accepting an argument, and the block 53 | # will be instance_eval'ed instead: 54 | # 55 | # @person.try { upcase.truncate(50) } 56 | # 57 | # Please also note that +try+ is defined on +Object+. Therefore, it won't work 58 | # with instances of classes that do not have +Object+ among their ancestors, 59 | # like direct subclasses of +BasicObject+. For example, using +try+ with 60 | # +SimpleDelegator+ will delegate +try+ to the target instead of calling it on 61 | # the delegator itself. 62 | def try(*a, &b) 63 | try!(*a, &b) if a.empty? || respond_to?(a.first) 64 | end 65 | 66 | # Same as #try, but will raise a NoMethodError exception if the receiver is not +nil+ and 67 | # does not implement the tried method. 68 | 69 | def try!(*a, &b) 70 | if a.empty? && block_given? 71 | if b.arity.zero? 72 | instance_eval(&b) 73 | else 74 | yield self 75 | end 76 | else 77 | public_send(*a, &b) 78 | end 79 | end 80 | end 81 | 82 | class NilClass 83 | # Calling +try+ on +nil+ always returns +nil+. 84 | # It becomes especially helpful when navigating through associations that may return +nil+. 85 | # 86 | # nil.try(:name) # => nil 87 | # 88 | # Without +try+ 89 | # @person && @person.children.any? && @person.children.first.name 90 | # 91 | # With +try+ 92 | # @person.try(:children).try(:first).try(:name) 93 | def try(*args) 94 | nil 95 | end 96 | 97 | def try!(*args) 98 | nil 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/array/grouping.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | # Splits or iterates over the array in groups of size +number+, 3 | # padding any remaining slots with +fill_with+ unless it is +false+. 4 | # 5 | # %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group} 6 | # ["1", "2", "3"] 7 | # ["4", "5", "6"] 8 | # ["7", "8", "9"] 9 | # ["10", nil, nil] 10 | # 11 | # %w(1 2 3 4 5).in_groups_of(2, ' ') {|group| p group} 12 | # ["1", "2"] 13 | # ["3", "4"] 14 | # ["5", " "] 15 | # 16 | # %w(1 2 3 4 5).in_groups_of(2, false) {|group| p group} 17 | # ["1", "2"] 18 | # ["3", "4"] 19 | # ["5"] 20 | def in_groups_of(number, fill_with = nil) 21 | if fill_with == false 22 | collection = self 23 | else 24 | # size % number gives how many extra we have; 25 | # subtracting from number gives how many to add; 26 | # modulo number ensures we don't add group of just fill. 27 | padding = (number - size % number) % number 28 | collection = dup.concat([fill_with] * padding) 29 | end 30 | 31 | if block_given? 32 | collection.each_slice(number) { |slice| yield(slice) } 33 | else 34 | groups = [] 35 | collection.each_slice(number) { |group| groups << group } 36 | groups 37 | end 38 | end 39 | 40 | # Splits or iterates over the array in +number+ of groups, padding any 41 | # remaining slots with +fill_with+ unless it is +false+. 42 | # 43 | # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group} 44 | # ["1", "2", "3", "4"] 45 | # ["5", "6", "7", nil] 46 | # ["8", "9", "10", nil] 47 | # 48 | # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3, ' ') {|group| p group} 49 | # ["1", "2", "3", "4"] 50 | # ["5", "6", "7", " "] 51 | # ["8", "9", "10", " "] 52 | # 53 | # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group} 54 | # ["1", "2", "3"] 55 | # ["4", "5"] 56 | # ["6", "7"] 57 | def in_groups(number, fill_with = nil) 58 | # size / number gives minor group size; 59 | # size % number gives how many objects need extra accommodation; 60 | # each group hold either division or division + 1 items. 61 | division = size.div number 62 | modulo = size % number 63 | 64 | # create a new array avoiding dup 65 | groups = [] 66 | start = 0 67 | 68 | number.times do |index| 69 | length = division + (modulo > 0 && modulo > index ? 1 : 0) 70 | groups << last_group = slice(start, length) 71 | last_group << fill_with if fill_with != false && 72 | modulo > 0 && length == division 73 | start += length 74 | end 75 | 76 | if block_given? 77 | groups.each { |g| yield(g) } 78 | else 79 | groups 80 | end 81 | end 82 | 83 | # Divides the array into one or more subarrays based on a delimiting +value+ 84 | # or the result of an optional block. 85 | # 86 | # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]] 87 | # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]] 88 | def split(value = nil, &block) 89 | inject([[]]) do |results, element| 90 | if block && block.call(element) || value == element 91 | results << [] 92 | else 93 | results.last << element 94 | end 95 | 96 | results 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /opal/active_support/inflector/inflections.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | 3 | module ActiveSupport 4 | module Inflector 5 | extend self 6 | 7 | class Inflections 8 | @__instance__ = {} 9 | 10 | class Uncountables < Array 11 | def <<(*word) 12 | add(word) 13 | end 14 | 15 | def add(words) 16 | words = words.flatten.map(&:downcase) 17 | concat(words) 18 | self 19 | end 20 | 21 | def uncountable?(str) 22 | include?(str.downcase) 23 | end 24 | 25 | def dup 26 | copy = Uncountables.new 27 | copy.add(self) 28 | copy 29 | end 30 | end 31 | 32 | 33 | def self.instance(locale) 34 | @__instance__[locale] ||= new 35 | end 36 | 37 | attr_reader :plurals, :singulars, :uncountables, :humans 38 | 39 | def initialize 40 | @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, [] 41 | end 42 | 43 | def initialize_dup(orig) # :nodoc: 44 | %w(plurals singulars uncountables humans).each do |scope| 45 | instance_variable_set("@#{scope}", orig.send(scope).dup) 46 | end 47 | end 48 | 49 | 50 | def plural(rule, replacement) 51 | @uncountables.delete(rule) if rule.is_a?(String) 52 | @uncountables.delete(replacement) 53 | @plurals.unshift([rule, replacement]) 54 | end 55 | 56 | def singular(rule, replacement) 57 | @uncountables.delete(rule) if rule.is_a?(String) 58 | @uncountables.delete(replacement) 59 | @singulars.unshift([rule, replacement]) 60 | end 61 | 62 | def uncountable(*words) 63 | @uncountables.add(words) 64 | end 65 | 66 | def human(rule, replacement) 67 | @humans.unshift([rule, replacement]) 68 | end 69 | 70 | def irregular(singular, plural) 71 | @uncountables.delete(singular) 72 | @uncountables.delete(plural) 73 | 74 | s0 = singular[0] 75 | srest = singular[1..-1] 76 | 77 | p0 = plural[0] 78 | prest = plural[1..-1] 79 | 80 | if s0.upcase == p0.upcase 81 | plural(/(#{s0})#{srest}$/i, '\1' + prest) 82 | plural(/(#{p0})#{prest}$/i, '\1' + prest) 83 | 84 | singular(/(#{s0})#{srest}$/i, '\1' + srest) 85 | singular(/(#{p0})#{prest}$/i, '\1' + srest) 86 | else 87 | plural(/#{s0.upcase}#{srest}$/i, p0.upcase + prest) 88 | plural(/#{s0.downcase}#{srest}$/i, p0.downcase + prest) 89 | plural(/#{p0.upcase}#{prest}$/i, p0.upcase + prest) 90 | plural(/#{p0.downcase}#{prest}$/i, p0.downcase + prest) 91 | 92 | singular(/#{s0.upcase}#{srest}$/i, s0.upcase + srest) 93 | singular(/#{s0.downcase}#{srest}$/i, s0.downcase + srest) 94 | singular(/#{p0.upcase}#{prest}$/i, s0.upcase + srest) 95 | singular(/#{p0.downcase}#{prest}$/i, s0.downcase + srest) 96 | end 97 | end 98 | 99 | def clear(scope = :all) 100 | case scope 101 | when :all 102 | @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, [] 103 | else 104 | instance_variable_set "@#{scope}", [] 105 | end 106 | end 107 | end 108 | 109 | def inflections(locale = :en) 110 | if block_given? 111 | yield Inflections.instance(locale) 112 | else 113 | Inflections.instance(locale) 114 | end 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /test/core_ext/kernel_test.rb: -------------------------------------------------------------------------------- 1 | require 'abstract_unit' 2 | require 'active_support/core_ext/kernel' 3 | 4 | class KernelTest < ActiveSupport::TestCase 5 | # def test_silence_warnings 6 | # silence_warnings { assert_nil $VERBOSE } 7 | # assert_equal 1234, silence_warnings { 1234 } 8 | # end 9 | # 10 | # def test_silence_warnings_verbose_invariant 11 | # old_verbose = $VERBOSE 12 | # silence_warnings { raise } 13 | # flunk 14 | # rescue 15 | # assert_equal old_verbose, $VERBOSE 16 | # end 17 | # 18 | # 19 | # def test_enable_warnings 20 | # enable_warnings { assert_equal true, $VERBOSE } 21 | # assert_equal 1234, enable_warnings { 1234 } 22 | # end 23 | # 24 | # def test_enable_warnings_verbose_invariant 25 | # old_verbose = $VERBOSE 26 | # enable_warnings { raise } 27 | # flunk 28 | # rescue 29 | # assert_equal old_verbose, $VERBOSE 30 | # end 31 | # 32 | # 33 | # def test_silence_stderr 34 | # old_stderr_position = STDERR.tell 35 | # silence_stderr { STDERR.puts 'hello world' } 36 | # assert_equal old_stderr_position, STDERR.tell 37 | # rescue Errno::ESPIPE 38 | # # Skip if we can't STDERR.tell 39 | # end 40 | # 41 | # def test_quietly 42 | # old_stdout_position, old_stderr_position = STDOUT.tell, STDERR.tell 43 | # quietly do 44 | # puts 'see me, feel me' 45 | # STDERR.puts 'touch me, heal me' 46 | # end 47 | # assert_equal old_stdout_position, STDOUT.tell 48 | # assert_equal old_stderr_position, STDERR.tell 49 | # rescue Errno::ESPIPE 50 | # # Skip if we can't STDERR.tell 51 | # end 52 | # 53 | # def test_silence_stderr_with_return_value 54 | # assert_equal 1, silence_stderr { 1 } 55 | # end 56 | 57 | def test_class_eval 58 | o = Object.new 59 | class << o; @x = 1; end 60 | assert_equal 1, o.class_eval { @x } 61 | end 62 | 63 | # def test_capture 64 | # assert_equal 'STDERR', capture(:stderr) { $stderr.print 'STDERR' } 65 | # assert_equal 'STDOUT', capture(:stdout) { print 'STDOUT' } 66 | # assert_equal "STDERR\n", capture(:stderr) { system('echo STDERR 1>&2') } 67 | # assert_equal "STDOUT\n", capture(:stdout) { system('echo STDOUT') } 68 | # end 69 | end 70 | 71 | # class KernelSuppressTest < ActiveSupport::TestCase 72 | # def test_reraise 73 | # assert_raise(LoadError) do 74 | # suppress(ArgumentError) { raise LoadError } 75 | # end 76 | # end 77 | # 78 | # def test_suppression 79 | # suppress(ArgumentError) { raise ArgumentError } 80 | # suppress(LoadError) { raise LoadError } 81 | # suppress(LoadError, ArgumentError) { raise LoadError } 82 | # suppress(LoadError, ArgumentError) { raise ArgumentError } 83 | # end 84 | # end 85 | # 86 | # class MockStdErr 87 | # attr_reader :output 88 | # def puts(message) 89 | # @output ||= [] 90 | # @output << message 91 | # end 92 | # 93 | # def info(message) 94 | # puts(message) 95 | # end 96 | # 97 | # def write(message) 98 | # puts(message) 99 | # end 100 | # end 101 | # 102 | # class KernelDebuggerTest < ActiveSupport::TestCase 103 | # def test_debugger_not_available_message_to_stderr 104 | # old_stderr = $stderr 105 | # $stderr = MockStdErr.new 106 | # debugger 107 | # assert_match(/Debugger requested/, $stderr.output.first) 108 | # ensure 109 | # $stderr = old_stderr 110 | # end 111 | # 112 | # def test_debugger_not_available_message_to_rails_logger 113 | # rails = Class.new do 114 | # def self.logger 115 | # @logger ||= MockStdErr.new 116 | # end 117 | # end 118 | # Object.const_set(:Rails, rails) 119 | # debugger 120 | # assert_match(/Debugger requested/, rails.logger.output.first) 121 | # ensure 122 | # Object.send(:remove_const, :Rails) 123 | # end 124 | # end 125 | -------------------------------------------------------------------------------- /test/concern_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | require "active_support/concern" 5 | 6 | class ConcernTest < ActiveSupport::TestCase 7 | module Baz 8 | extend ActiveSupport::Concern 9 | 10 | class_methods do 11 | def baz 12 | "baz" 13 | end 14 | 15 | def included_ran=(value) 16 | @@included_ran = value 17 | end 18 | 19 | def included_ran 20 | @@included_ran 21 | end 22 | end 23 | 24 | included do 25 | self.included_ran = true 26 | end 27 | 28 | def baz 29 | "baz" 30 | end 31 | end 32 | 33 | module Bar 34 | extend ActiveSupport::Concern 35 | 36 | include Baz 37 | 38 | module ClassMethods 39 | def baz 40 | "bar's baz + " + super 41 | end 42 | end 43 | 44 | def bar 45 | "bar" 46 | end 47 | 48 | def baz 49 | "bar+" + super 50 | end 51 | end 52 | 53 | module Foo 54 | extend ActiveSupport::Concern 55 | 56 | include Bar, Baz 57 | end 58 | 59 | module Qux 60 | module ClassMethods 61 | end 62 | end 63 | 64 | def setup 65 | @klass = Class.new 66 | end 67 | 68 | def test_module_is_included_normally 69 | @klass.include(Baz) 70 | assert_equal "baz", @klass.new.baz 71 | assert_includes @klass.included_modules, ConcernTest::Baz 72 | end 73 | 74 | def test_class_methods_are_extended 75 | @klass.include(Baz) 76 | assert_equal "baz", @klass.baz 77 | assert_equal ConcernTest::Baz::ClassMethods, (class << @klass; included_modules; end)[0] 78 | end 79 | 80 | def test_class_methods_are_extended_only_on_expected_objects 81 | ::Object.include(Qux) 82 | Object.extend(Qux::ClassMethods) 83 | # module needs to be created after Qux is included in Object or bug won't 84 | # be triggered 85 | test_module = Module.new do 86 | extend ActiveSupport::Concern 87 | 88 | class_methods do 89 | def test 90 | end 91 | end 92 | end 93 | @klass.include test_module 94 | # NOTE: Opal minitest doesn't have assert_not_respond_to 95 | # assert_not_respond_to Object, :test 96 | assert_equal false, Object.respond_to?(:test) 97 | Qux.class_eval do 98 | remove_const :ClassMethods 99 | end 100 | end 101 | 102 | def test_included_block_is_ran 103 | @klass.include(Baz) 104 | assert_equal true, @klass.included_ran 105 | end 106 | 107 | def test_modules_dependencies_are_met 108 | @klass.include(Bar) 109 | assert_equal "bar", @klass.new.bar 110 | assert_equal "bar+baz", @klass.new.baz 111 | assert_equal "bar's baz + baz", @klass.baz 112 | assert_includes @klass.included_modules, ConcernTest::Bar 113 | end 114 | 115 | def test_dependencies_with_multiple_modules 116 | @klass.include(Foo) 117 | # FIXME: This is how the test was originally written but it throws a weird error: 118 | # ArgumentError: unknown encoding name - 119 | # at singleton_class_alloc.$$find [as $find] 120 | # 121 | # assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..2] 122 | # 123 | # Also the order of the indluded_modules is backwards in Opal. 124 | # So I've rewritten like so. 125 | assert_equal 3, @klass.included_modules[0..2].size 126 | @klass.included_modules[0..2].each do |mod| 127 | assert_includes [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], mod 128 | end 129 | end 130 | 131 | def test_raise_on_multiple_included_calls 132 | assert_raises(ActiveSupport::Concern::MultipleIncludedBlocks) do 133 | Module.new do 134 | extend ActiveSupport::Concern 135 | 136 | included do 137 | end 138 | 139 | included do 140 | end 141 | end 142 | end 143 | end 144 | end 145 | -------------------------------------------------------------------------------- /opal/active_support/concern.rb: -------------------------------------------------------------------------------- 1 | module ActiveSupport 2 | # A typical module looks like this: 3 | # 4 | # module M 5 | # def self.included(base) 6 | # base.extend ClassMethods 7 | # base.class_eval do 8 | # scope :disabled, -> { where(disabled: true) } 9 | # end 10 | # end 11 | # 12 | # module ClassMethods 13 | # ... 14 | # end 15 | # end 16 | # 17 | # By using ActiveSupport::Concern the above module could instead be 18 | # written as: 19 | # 20 | # require 'active_support/concern' 21 | # 22 | # module M 23 | # extend ActiveSupport::Concern 24 | # 25 | # included do 26 | # scope :disabled, -> { where(disabled: true) } 27 | # end 28 | # 29 | # class_methods do 30 | # ... 31 | # end 32 | # end 33 | # 34 | # Moreover, it gracefully handles module dependencies. Given a +Foo+ module 35 | # and a +Bar+ module which depends on the former, we would typically write the 36 | # following: 37 | # 38 | # module Foo 39 | # def self.included(base) 40 | # base.class_eval do 41 | # def self.method_injected_by_foo 42 | # ... 43 | # end 44 | # end 45 | # end 46 | # end 47 | # 48 | # module Bar 49 | # def self.included(base) 50 | # base.method_injected_by_foo 51 | # end 52 | # end 53 | # 54 | # class Host 55 | # include Foo # We need to include this dependency for Bar 56 | # include Bar # Bar is the module that Host really needs 57 | # end 58 | # 59 | # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We 60 | # could try to hide these from +Host+ directly including +Foo+ in +Bar+: 61 | # 62 | # module Bar 63 | # include Foo 64 | # def self.included(base) 65 | # base.method_injected_by_foo 66 | # end 67 | # end 68 | # 69 | # class Host 70 | # include Bar 71 | # end 72 | # 73 | # Unfortunately this won't work, since when +Foo+ is included, its base 74 | # is the +Bar+ module, not the +Host+ class. With ActiveSupport::Concern, 75 | # module dependencies are properly resolved: 76 | # 77 | # require 'active_support/concern' 78 | # 79 | # module Foo 80 | # extend ActiveSupport::Concern 81 | # included do 82 | # def self.method_injected_by_foo 83 | # ... 84 | # end 85 | # end 86 | # end 87 | # 88 | # module Bar 89 | # extend ActiveSupport::Concern 90 | # include Foo 91 | # 92 | # included do 93 | # self.method_injected_by_foo 94 | # end 95 | # end 96 | # 97 | # class Host 98 | # include Bar # It works, now Bar takes care of its dependencies 99 | # end 100 | module Concern 101 | class MultipleIncludedBlocks < StandardError #:nodoc: 102 | # Opal 0.11 always passes an argument to Exception.exception 103 | def initialize(_) 104 | super "Cannot define multiple 'included' blocks for a Concern" 105 | end 106 | end 107 | 108 | def self.extended(base) #:nodoc: 109 | base.instance_variable_set(:@_dependencies, []) 110 | end 111 | 112 | def append_features(base) 113 | if base.instance_variable_defined?(:@_dependencies) 114 | base.instance_variable_get(:@_dependencies) << self 115 | false 116 | else 117 | return false if base < self 118 | @_dependencies.each { |dep| base.include(dep) } 119 | super 120 | base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) 121 | # if instance_variable_defined?(:@_included_block) 122 | # base.class_eval(&@_included_block) 123 | # end 124 | if @_included_block 125 | base.class_eval(&@_included_block) 126 | end 127 | end 128 | end 129 | 130 | def included(base = nil, &block) 131 | if base.nil? 132 | if instance_variable_defined?(:@_included_block) 133 | raise MultipleIncludedBlocks 134 | end 135 | 136 | @_included_block = block 137 | else 138 | super 139 | end 140 | end 141 | 142 | def class_methods(&class_methods_module_definition) 143 | mod = if const_defined?(:ClassMethods, false) 144 | const_get(:ClassMethods) 145 | else 146 | const_set(:ClassMethods, Module.new) 147 | end 148 | 149 | mod.module_eval(&class_methods_module_definition) 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /test/constantize_test_cases.rb: -------------------------------------------------------------------------------- 1 | module Ace 2 | module Base 3 | class Case 4 | class Dice 5 | end 6 | end 7 | class Fase < Case 8 | end 9 | end 10 | class Gas 11 | include Base 12 | end 13 | end 14 | 15 | class Object 16 | module AddtlGlobalConstants 17 | class Case 18 | class Dice 19 | end 20 | end 21 | end 22 | include AddtlGlobalConstants 23 | end 24 | 25 | module ConstantizeTestCases 26 | def run_constantize_tests_on 27 | assert_equal Ace::Base::Case, yield("Ace::Base::Case") 28 | assert_equal Ace::Base::Case, yield("::Ace::Base::Case") 29 | assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice") 30 | assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Fase::Dice") 31 | assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice") 32 | 33 | assert_equal Ace::Gas::Case, yield("Ace::Gas::Case") 34 | assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice") 35 | assert_equal Ace::Base::Case::Dice, yield("Ace::Gas::Case::Dice") 36 | 37 | assert_equal Case::Dice, yield("Case::Dice") 38 | assert_equal AddtlGlobalConstants::Case::Dice, yield("Case::Dice") 39 | assert_equal Object::AddtlGlobalConstants::Case::Dice, yield("Case::Dice") 40 | 41 | assert_equal Case::Dice, yield("Object::Case::Dice") 42 | assert_equal AddtlGlobalConstants::Case::Dice, yield("Object::Case::Dice") 43 | assert_equal Object::AddtlGlobalConstants::Case::Dice, yield("Case::Dice") 44 | 45 | assert_equal ConstantizeTestCases, yield("ConstantizeTestCases") 46 | assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases") 47 | 48 | assert_raises(NameError) { yield("UnknownClass") } 49 | assert_raises(NameError) { yield("UnknownClass::Ace") } 50 | assert_raises(NameError) { yield("UnknownClass::Ace::Base") } 51 | assert_raises(NameError) { yield("An invalid string") } 52 | assert_raises(NameError) { yield("InvalidClass\n") } 53 | assert_raises(NameError) { yield("Ace::ConstantizeTestCases") } 54 | assert_raises(NameError) { yield("Ace::Base::ConstantizeTestCases") } 55 | assert_raises(NameError) { yield("Ace::Gas::Base") } 56 | assert_raises(NameError) { yield("Ace::Gas::ConstantizeTestCases") } 57 | assert_raises(NameError) { yield("") } 58 | assert_raises(NameError) { yield("::") } 59 | assert_raises(NameError) { yield("Ace::gas") } 60 | 61 | # assert_raises(NameError) do 62 | # with_autoloading_fixtures do 63 | # yield("RaisesNameError") 64 | # end 65 | # end 66 | 67 | # assert_raises(NoMethodError) do 68 | # with_autoloading_fixtures do 69 | # yield("RaisesNoMethodError") 70 | # end 71 | # end 72 | 73 | # with_autoloading_fixtures do 74 | # yield("Prepend::SubClassConflict") 75 | # assert_equal "constant", defined?(Prepend::SubClassConflict) 76 | # end 77 | end 78 | 79 | def run_safe_constantize_tests_on 80 | assert_equal Ace::Base::Case, yield("Ace::Base::Case") 81 | assert_equal Ace::Base::Case, yield("::Ace::Base::Case") 82 | assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice") 83 | assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice") 84 | assert_equal Ace::Gas::Case, yield("Ace::Gas::Case") 85 | assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice") 86 | assert_equal Case::Dice, yield("Case::Dice") 87 | assert_equal Case::Dice, yield("Object::Case::Dice") 88 | assert_equal ConstantizeTestCases, yield("ConstantizeTestCases") 89 | assert_equal ConstantizeTestCases, yield("::ConstantizeTestCases") 90 | assert_nil yield("") 91 | assert_nil yield("::") 92 | assert_nil yield("UnknownClass") 93 | assert_nil yield("UnknownClass::Ace") 94 | assert_nil yield("UnknownClass::Ace::Base") 95 | assert_nil yield("An invalid string") 96 | assert_nil yield("InvalidClass\n") 97 | assert_nil yield("blargle") 98 | assert_nil yield("Ace::ConstantizeTestCases") 99 | assert_nil yield("Ace::Base::ConstantizeTestCases") 100 | assert_nil yield("Ace::Gas::Base") 101 | assert_nil yield("Ace::Gas::ConstantizeTestCases") 102 | assert_nil yield("#::Nested_1") 103 | assert_nil yield("Ace::gas") 104 | assert_nil yield("Object::ABC") 105 | assert_nil yield("Object::Object::Object::ABC") 106 | assert_nil yield("A::Object::B") 107 | assert_nil yield("A::Object::Object::Object::B") 108 | 109 | # assert_raises(NameError) do 110 | # with_autoloading_fixtures do 111 | # yield("RaisesNameError") 112 | # end 113 | # end 114 | 115 | # assert_raises(NoMethodError) do 116 | # with_autoloading_fixtures do 117 | # yield("RaisesNoMethodError") 118 | # end 119 | # end 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/class/attribute.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/kernel/singleton_class' 2 | require 'active_support/core_ext/module/remove_method' 3 | require 'active_support/core_ext/array/extract_options' 4 | 5 | class Class 6 | # Declare a class-level attribute whose value is inheritable by subclasses. 7 | # Subclasses can change their own value and it will not impact parent class. 8 | # 9 | # class Base 10 | # class_attribute :setting 11 | # end 12 | # 13 | # class Subclass < Base 14 | # end 15 | # 16 | # Base.setting = true 17 | # Subclass.setting # => true 18 | # Subclass.setting = false 19 | # Subclass.setting # => false 20 | # Base.setting # => true 21 | # 22 | # In the above case as long as Subclass does not assign a value to setting 23 | # by performing Subclass.setting = _something_ , Subclass.setting 24 | # would read value assigned to parent class. Once Subclass assigns a value then 25 | # the value assigned by Subclass would be returned. 26 | # 27 | # This matches normal Ruby method inheritance: think of writing an attribute 28 | # on a subclass as overriding the reader method. However, you need to be aware 29 | # when using +class_attribute+ with mutable structures as +Array+ or +Hash+. 30 | # In such cases, you don't want to do changes in places but use setters: 31 | # 32 | # Base.setting = [] 33 | # Base.setting # => [] 34 | # Subclass.setting # => [] 35 | # 36 | # # Appending in child changes both parent and child because it is the same object: 37 | # Subclass.setting << :foo 38 | # Base.setting # => [:foo] 39 | # Subclass.setting # => [:foo] 40 | # 41 | # # Use setters to not propagate changes: 42 | # Base.setting = [] 43 | # Subclass.setting += [:foo] 44 | # Base.setting # => [] 45 | # Subclass.setting # => [:foo] 46 | # 47 | # For convenience, an instance predicate method is defined as well. 48 | # To skip it, pass instance_predicate: false. 49 | # 50 | # Subclass.setting? # => false 51 | # 52 | # Instances may overwrite the class value in the same way: 53 | # 54 | # Base.setting = true 55 | # object = Base.new 56 | # object.setting # => true 57 | # object.setting = false 58 | # object.setting # => false 59 | # Base.setting # => true 60 | # 61 | # To opt out of the instance reader method, pass instance_reader: false. 62 | # 63 | # object.setting # => NoMethodError 64 | # object.setting? # => NoMethodError 65 | # 66 | # To opt out of the instance writer method, pass instance_writer: false. 67 | # 68 | # object.setting = false # => NoMethodError 69 | # 70 | # To opt out of both instance methods, pass instance_accessor: false. 71 | def class_attribute(*attrs) 72 | options = attrs.extract_options! 73 | instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true) 74 | instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true) 75 | instance_predicate = options.fetch(:instance_predicate, true) 76 | 77 | attrs.each do |name| 78 | define_singleton_method(name) { nil } 79 | define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate 80 | 81 | ivar = "@#{name}" 82 | 83 | define_singleton_method("#{name}=") do |val| 84 | singleton_class.class_eval do 85 | remove_possible_method(name) 86 | define_method(name) { val } 87 | end 88 | 89 | if singleton_class? 90 | class_eval do 91 | remove_possible_method(name) 92 | define_method(name) do 93 | if instance_variable_defined? ivar 94 | instance_variable_get ivar 95 | else 96 | singleton_class.send name 97 | end 98 | end 99 | end 100 | end 101 | val 102 | end 103 | 104 | if instance_reader 105 | remove_possible_method name 106 | define_method(name) do 107 | if instance_variable_defined?(ivar) 108 | instance_variable_get ivar 109 | else 110 | self.class.public_send name 111 | end 112 | end 113 | define_method("#{name}?") { !!public_send(name) } if instance_predicate 114 | end 115 | 116 | attr_writer name if instance_writer 117 | end 118 | end 119 | 120 | private 121 | 122 | unless respond_to?(:singleton_class?) 123 | def singleton_class? 124 | # Opal doesn't place singleton at ancestors.first. Returning true here 125 | # seems to work in Opal anyway. 126 | true || ancestors.first != self 127 | end 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /test/core_ext/object_and_class_ext_test.rb: -------------------------------------------------------------------------------- 1 | require 'abstract_unit' 2 | require 'active_support/time' 3 | require 'active_support/core_ext/object' 4 | # require 'active_support/core_ext/class/subclasses' 5 | # 6 | # class ClassA; end 7 | # class ClassB < ClassA; end 8 | # class ClassC < ClassB; end 9 | # class ClassD < ClassA; end 10 | # 11 | # class ClassI; end 12 | # class ClassJ < ClassI; end 13 | # 14 | # class ClassK 15 | # end 16 | # module Nested 17 | # class << self 18 | # def on_const_missing(&callback) 19 | # @on_const_missing = callback 20 | # end 21 | # private 22 | # def const_missing(mod_id) 23 | # @on_const_missing[mod_id] if @on_const_missing 24 | # super 25 | # end 26 | # end 27 | # class ClassL < ClassK 28 | # end 29 | # end 30 | # 31 | # class ObjectTests < ActiveSupport::TestCase 32 | # class DuckTime 33 | # def acts_like_time? 34 | # true 35 | # end 36 | # end 37 | # 38 | # def test_duck_typing 39 | # object = Object.new 40 | # time = Time.now 41 | # date = Date.today 42 | # dt = DateTime.new 43 | # duck = DuckTime.new 44 | # 45 | # assert !object.acts_like?(:time) 46 | # assert !object.acts_like?(:date) 47 | # 48 | # assert time.acts_like?(:time) 49 | # assert !time.acts_like?(:date) 50 | # 51 | # assert !date.acts_like?(:time) 52 | # assert date.acts_like?(:date) 53 | # 54 | # assert dt.acts_like?(:time) 55 | # assert dt.acts_like?(:date) 56 | # 57 | # assert duck.acts_like?(:time) 58 | # assert !duck.acts_like?(:date) 59 | # end 60 | # end 61 | # 62 | # class ObjectInstanceVariableTest < ActiveSupport::TestCase 63 | # def setup 64 | # @source, @dest = Object.new, Object.new 65 | # @source.instance_variable_set(:@bar, 'bar') 66 | # @source.instance_variable_set(:@baz, 'baz') 67 | # end 68 | # 69 | # def test_instance_variable_names 70 | # assert_equal %w(@bar @baz), @source.instance_variable_names.sort 71 | # end 72 | # 73 | # def test_instance_values 74 | # object = Object.new 75 | # object.instance_variable_set :@a, 1 76 | # object.instance_variable_set :@b, 2 77 | # assert_equal({'a' => 1, 'b' => 2}, object.instance_values) 78 | # end 79 | # 80 | # def test_instance_exec_passes_arguments_to_block 81 | # assert_equal %w(hello goodbye), 'hello'.instance_exec('goodbye') { |v| [self, v] } 82 | # end 83 | # 84 | # def test_instance_exec_with_frozen_obj 85 | # assert_equal %w(olleh goodbye), 'hello'.freeze.instance_exec('goodbye') { |v| [reverse, v] } 86 | # end 87 | # 88 | # def test_instance_exec_nested 89 | # assert_equal %w(goodbye olleh bar), 'hello'.instance_exec('goodbye') { |arg| 90 | # [arg] + instance_exec('bar') { |v| [reverse, v] } } 91 | # end 92 | # end 93 | 94 | class ObjectTryTest < ActiveSupport::TestCase 95 | def setup 96 | @string = "Hello" 97 | end 98 | 99 | def test_nonexisting_method 100 | method = :undefined_method 101 | assert !@string.respond_to?(method) 102 | assert_nil @string.try(method) 103 | end 104 | 105 | def test_nonexisting_method_with_arguments 106 | method = :undefined_method 107 | assert !@string.respond_to?(method) 108 | assert_nil @string.try(method, 'llo', 'y') 109 | end 110 | 111 | def test_nonexisting_method_bang 112 | method = :undefined_method 113 | assert !@string.respond_to?(method) 114 | assert_raise(NoMethodError) { @string.try!(method) } 115 | end 116 | 117 | def test_nonexisting_method_with_arguments_bang 118 | method = :undefined_method 119 | assert !@string.respond_to?(method) 120 | assert_raise(NoMethodError) { @string.try!(method, 'llo', 'y') } 121 | end 122 | 123 | def test_try_only_block_bang 124 | assert_equal @string.reverse, @string.try! { |s| s.reverse } 125 | end 126 | 127 | def test_valid_method 128 | assert_equal 5, @string.try(:size) 129 | end 130 | 131 | def test_argument_forwarding 132 | assert_equal 'Hey', @string.try(:sub, 'llo', 'y') 133 | end 134 | 135 | def test_block_forwarding 136 | assert_equal 'Hey', @string.try(:sub, 'llo') { |match| 'y' } 137 | end 138 | 139 | def test_nil_to_type 140 | assert_nil nil.try(:to_s) 141 | assert_nil nil.try(:to_i) 142 | end 143 | 144 | def test_false_try 145 | assert_equal 'false', false.try(:to_s) 146 | end 147 | 148 | def test_try_only_block 149 | assert_equal @string.reverse, @string.try { |s| s.reverse } 150 | end 151 | 152 | def test_try_only_block_nil 153 | ran = false 154 | nil.try { ran = true } 155 | assert_equal false, ran 156 | end 157 | 158 | # Opal doesn't support private methods 159 | # def test_try_with_private_method_bang 160 | # klass = Class.new do 161 | # private 162 | # 163 | # def private_method 164 | # 'private method' 165 | # end 166 | # end 167 | # 168 | # assert_raise(NoMethodError) { klass.new.try!(:private_method) } 169 | # end 170 | 171 | # Opal doesn't support private methods 172 | # def test_try_with_private_method 173 | # klass = Class.new do 174 | # private 175 | # 176 | # def private_method 177 | # 'private method' 178 | # end 179 | # end 180 | # 181 | # assert_nil klass.new.try(:private_method) 182 | # end 183 | end 184 | -------------------------------------------------------------------------------- /opal/active_support/core_ext/string/filters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class String 4 | # Returns the string, first removing all whitespace on both ends of 5 | # the string, and then changing remaining consecutive whitespace 6 | # groups into one space each. 7 | # 8 | # Note that it handles both ASCII and Unicode whitespace. 9 | # 10 | # %{ Multi-line 11 | # string }.squish # => "Multi-line string" 12 | # " foo bar \n \t boo".squish # => "foo bar boo" 13 | # def squish 14 | # dup.squish! 15 | # end 16 | 17 | # Performs a destructive squish. See String#squish. 18 | # str = " foo bar \n \t boo" 19 | # str.squish! # => "foo bar boo" 20 | # str # => "foo bar boo" 21 | # def squish! 22 | # gsub!(/[[:space:]]+/, " ") 23 | # strip! 24 | # self 25 | # end 26 | 27 | # Returns a new string with all occurrences of the patterns removed. 28 | # str = "foo bar test" 29 | # str.remove(" test") # => "foo bar" 30 | # str.remove(" test", /bar/) # => "foo " 31 | # str # => "foo bar test" 32 | # def remove(*patterns) 33 | # dup.remove!(*patterns) 34 | # end 35 | 36 | # Alters the string by removing all occurrences of the patterns. 37 | # str = "foo bar test" 38 | # str.remove!(" test", /bar/) # => "foo " 39 | # str # => "foo " 40 | # def remove!(*patterns) 41 | # patterns.each do |pattern| 42 | # gsub! pattern, "" 43 | # end 44 | # 45 | # self 46 | # end 47 | 48 | # Truncates a given +text+ after a given length if +text+ is longer than length: 49 | # 50 | # 'Once upon a time in a world far far away'.truncate(27) 51 | # # => "Once upon a time in a wo..." 52 | # 53 | # Pass a string or regexp :separator to truncate +text+ at a natural break: 54 | # 55 | # 'Once upon a time in a world far far away'.truncate(27, separator: ' ') 56 | # # => "Once upon a time in a..." 57 | # 58 | # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/) 59 | # # => "Once upon a time in a..." 60 | # 61 | # The last characters will be replaced with the :omission string (defaults to "...") 62 | # for a total length not exceeding length: 63 | # 64 | # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)') 65 | # # => "And they f... (continued)" 66 | def truncate(truncate_at, options = {}) 67 | return dup unless length > truncate_at 68 | 69 | omission = options[:omission] || "..." 70 | length_with_room_for_omission = truncate_at - omission.length 71 | stop = \ 72 | if options[:separator] 73 | rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission 74 | else 75 | length_with_room_for_omission 76 | end 77 | 78 | "#{self[0, stop]}#{omission}" 79 | end 80 | 81 | # Truncates +text+ to at most bytesize bytes in length without 82 | # breaking string encoding by splitting multibyte characters or breaking 83 | # grapheme clusters ("perceptual characters") by truncating at combining 84 | # characters. 85 | # 86 | # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".size 87 | # => 20 88 | # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".bytesize 89 | # => 80 90 | # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".truncate_bytes(20) 91 | # => "🔪🔪🔪🔪…" 92 | # 93 | # The truncated text ends with the :omission string, defaulting 94 | # to "…", for a total length not exceeding bytesize. 95 | # def truncate_bytes(truncate_at, omission: "…") 96 | # omission ||= "" 97 | # 98 | # case 99 | # when bytesize <= truncate_at 100 | # dup 101 | # when omission.bytesize > truncate_at 102 | # raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{truncate_at} bytes" 103 | # when omission.bytesize == truncate_at 104 | # omission.dup 105 | # else 106 | # self.class.new.tap do |cut| 107 | # cut_at = truncate_at - omission.bytesize 108 | # 109 | # scan(/\X/) do |grapheme| 110 | # if cut.bytesize + grapheme.bytesize <= cut_at 111 | # cut << grapheme 112 | # else 113 | # break 114 | # end 115 | # end 116 | # 117 | # cut << omission 118 | # end 119 | # end 120 | # end 121 | 122 | # Truncates a given +text+ after a given number of words (words_count): 123 | # 124 | # 'Once upon a time in a world far far away'.truncate_words(4) 125 | # # => "Once upon a time..." 126 | # 127 | # Pass a string or regexp :separator to specify a different separator of words: 128 | # 129 | # 'Once
upon
a
time
in
a
world'.truncate_words(5, separator: '
') 130 | # # => "Once
upon
a
time
in..." 131 | # 132 | # The last characters will be replaced with the :omission string (defaults to "..."): 133 | # 134 | # 'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)') 135 | # # => "And they found that many... (continued)" 136 | # def truncate_words(words_count, options = {}) 137 | # sep = options[:separator] || /\s+/ 138 | # sep = Regexp.escape(sep.to_s) unless Regexp === sep 139 | # if self =~ /\A((?>.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m 140 | # $1 + (options[:omission] || "...") 141 | # else 142 | # dup 143 | # end 144 | # end 145 | end 146 | 147 | -------------------------------------------------------------------------------- /opal/active_support/inflector/methods.rb: -------------------------------------------------------------------------------- 1 | require "active_support/inflections" 2 | 3 | module ActiveSupport 4 | module Inflector 5 | extend self 6 | 7 | def pluralize(word, locale = :en) 8 | apply_inflections(word, inflections(locale).plurals, locale) 9 | end 10 | 11 | def singularize(word, locale = :en) 12 | apply_inflections(word, inflections(locale).singulars, locale) 13 | end 14 | 15 | def camelize(term, uppercase_first_letter = true) 16 | string = term.to_s 17 | if uppercase_first_letter 18 | string = string.sub(/^[a-z\d]*/) { |match| match.capitalize } 19 | else 20 | string = string.downcase 21 | end 22 | string = string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" } 23 | string = string.gsub("/".freeze, "::".freeze) 24 | string 25 | end 26 | 27 | def underscore(camel_cased_word) 28 | return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word) 29 | word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze) 30 | word = word.gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze) 31 | word = word.gsub(/([a-z\d])([A-Z])/, '\1_\2'.freeze) 32 | word = word.tr("-".freeze, "_".freeze) 33 | word = word.downcase 34 | word 35 | end 36 | 37 | def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false) 38 | result = lower_case_and_underscored_word.to_s.dup 39 | 40 | inflections.humans.each do |(rule, replacement)| 41 | if (rule.is_a?(Regexp) && result =~ rule) || (rule.is_a?(String) && result == rule) 42 | result = result.sub(rule, replacement) 43 | break 44 | end 45 | end 46 | 47 | result = result.sub(/\A_+/, "".freeze) 48 | unless keep_id_suffix 49 | result = result.sub(/_id\z/, "".freeze) 50 | end 51 | result = result.tr("_".freeze, " ".freeze) 52 | 53 | result = result.gsub(/([a-z\d]*)/i) do |match| 54 | "#{match.downcase}" 55 | end 56 | 57 | if capitalize 58 | result = result.sub(/\A\w/) { |match| match.upcase } 59 | end 60 | 61 | result 62 | end 63 | 64 | def upcase_first(string) 65 | string.length > 0 ? string[0].upcase + string[1..-1] : "" 66 | end 67 | 68 | def titleize(word, keep_id_suffix: false) 69 | humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/([a-zA-Z'’`])[a-z]*/) do |match| 70 | match.capitalize 71 | end 72 | end 73 | 74 | def tableize(class_name) 75 | pluralize(underscore(class_name)) 76 | end 77 | 78 | def classify(table_name) 79 | # strip out any leading schema name 80 | camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze))) 81 | end 82 | 83 | def dasherize(underscored_word) 84 | underscored_word.tr("_".freeze, "-".freeze) 85 | end 86 | 87 | def demodulize(path) 88 | path = path.to_s 89 | if i = path.rindex("::") 90 | path[(i + 2)..-1] 91 | else 92 | path 93 | end 94 | end 95 | 96 | def deconstantize(path) 97 | path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename 98 | end 99 | 100 | def foreign_key(class_name, separate_class_name_and_id_with_underscore = true) 101 | underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id") 102 | end 103 | 104 | def constantize(camel_cased_word) 105 | names = camel_cased_word.split('::') 106 | 107 | # Trigger a built-in NameError exception including the ill-formed constant in the message. 108 | # FIXME: initially was 109 | # Object.const_get(camel_cased_word) if names.empty? 110 | # Changed because Opal can't handles such case 111 | # Instead it throws something like 112 | raise NameError, 'wrong constant name ' if names.empty? 113 | 114 | # Remove the first blank element in case of '::ClassName' notation. 115 | names.shift if names.size > 1 && names.first.empty? 116 | 117 | names.inject(Object) do |constant, name| 118 | if constant == Object 119 | constant.const_get(name) 120 | else 121 | candidate = constant.const_get(name) 122 | next candidate if constant.const_defined?(name, false) 123 | next candidate unless Object.const_defined?(name) 124 | 125 | # Go down the ancestors to check if it is owned directly. The check 126 | # stops when we reach Object or the end of ancestors tree. 127 | constant = constant.ancestors.inject(constant) do |const, ancestor| 128 | break const if ancestor == Object 129 | break ancestor if ancestor.const_defined?(name, false) 130 | const 131 | end 132 | 133 | # owner is in Object, so raise 134 | constant.const_get(name, false) 135 | end 136 | end 137 | end 138 | 139 | def safe_constantize(camel_cased_word) 140 | constantize(camel_cased_word) 141 | rescue NameError => e 142 | raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) || 143 | e.name.to_s == camel_cased_word.to_s) 144 | rescue ArgumentError => e 145 | raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message) 146 | end 147 | 148 | 149 | def ordinal(number) 150 | abs_number = number.to_i.abs 151 | 152 | if (11..13).include?(abs_number % 100) 153 | "th" 154 | else 155 | case abs_number % 10 156 | when 1; "st" 157 | when 2; "nd" 158 | when 3; "rd" 159 | else "th" 160 | end 161 | end 162 | end 163 | 164 | def ordinalize(number) 165 | "#{number}#{ordinal(number)}" 166 | end 167 | 168 | private 169 | 170 | def const_regexp(camel_cased_word) 171 | parts = camel_cased_word.split("::".freeze) 172 | 173 | return Regexp.escape(camel_cased_word) if parts.blank? 174 | 175 | last = parts.pop 176 | 177 | parts.reverse.inject(last) do |acc, part| 178 | part.empty? ? acc : "#{part}(::#{acc})?" 179 | end 180 | end 181 | 182 | def apply_inflections(word, rules, locale = :en) 183 | result = word.to_s.dup 184 | 185 | if word.empty? || inflections(locale).uncountables.uncountable?(result) 186 | result 187 | else 188 | rules.each do |(rule, replacement)| 189 | if (rule.is_a?(Regexp) && result =~ rule) || (rule.is_a?(String) && result == rule) 190 | result = result.sub(rule, replacement) 191 | break 192 | end 193 | end 194 | result 195 | end 196 | end 197 | end 198 | end 199 | -------------------------------------------------------------------------------- /test/inflector_test_cases.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module InflectorTestCases 4 | SingularToPlural = { 5 | "search" => "searches", 6 | "switch" => "switches", 7 | "fix" => "fixes", 8 | "box" => "boxes", 9 | "process" => "processes", 10 | "address" => "addresses", 11 | "case" => "cases", 12 | "stack" => "stacks", 13 | "wish" => "wishes", 14 | "fish" => "fish", 15 | "jeans" => "jeans", 16 | # "funky jeans" => "funky jeans", 17 | # "my money" => "my money", 18 | 19 | "category" => "categories", 20 | "query" => "queries", 21 | "ability" => "abilities", 22 | "agency" => "agencies", 23 | "movie" => "movies", 24 | 25 | "archive" => "archives", 26 | 27 | "index" => "indices", 28 | 29 | "wife" => "wives", 30 | "safe" => "saves", 31 | "half" => "halves", 32 | 33 | "move" => "moves", 34 | 35 | "salesperson" => "salespeople", 36 | "person" => "people", 37 | 38 | "spokesman" => "spokesmen", 39 | "man" => "men", 40 | "woman" => "women", 41 | 42 | "basis" => "bases", 43 | "diagnosis" => "diagnoses", 44 | "diagnosis_a" => "diagnosis_as", 45 | 46 | "datum" => "data", 47 | "medium" => "media", 48 | "stadium" => "stadia", 49 | "analysis" => "analyses", 50 | "my_analysis" => "my_analyses", 51 | 52 | "node_child" => "node_children", 53 | "child" => "children", 54 | 55 | "experience" => "experiences", 56 | "day" => "days", 57 | 58 | "comment" => "comments", 59 | "foobar" => "foobars", 60 | "newsletter" => "newsletters", 61 | 62 | "old_news" => "old_news", 63 | "news" => "news", 64 | 65 | "series" => "series", 66 | "miniseries" => "miniseries", 67 | "species" => "species", 68 | 69 | "quiz" => "quizzes", 70 | 71 | "perspective" => "perspectives", 72 | 73 | "ox" => "oxen", 74 | "photo" => "photos", 75 | "buffalo" => "buffaloes", 76 | "tomato" => "tomatoes", 77 | "dwarf" => "dwarves", 78 | "elf" => "elves", 79 | "information" => "information", 80 | "equipment" => "equipment", 81 | "bus" => "buses", 82 | "status" => "statuses", 83 | "status_code" => "status_codes", 84 | "mouse" => "mice", 85 | 86 | "louse" => "lice", 87 | "house" => "houses", 88 | "octopus" => "octopi", 89 | "virus" => "viri", 90 | "alias" => "aliases", 91 | "portfolio" => "portfolios", 92 | 93 | "vertex" => "vertices", 94 | "matrix" => "matrices", 95 | "matrix_fu" => "matrix_fus", 96 | 97 | "axis" => "axes", 98 | "taxi" => "taxis", # prevents regression 99 | "testis" => "testes", 100 | "crisis" => "crises", 101 | 102 | "rice" => "rice", 103 | "shoe" => "shoes", 104 | 105 | "horse" => "horses", 106 | "prize" => "prizes", 107 | "edge" => "edges", 108 | 109 | "database" => "databases", 110 | 111 | # regression tests against improper inflection regexes 112 | "|ice" => "|ices", 113 | "|ouse" => "|ouses", 114 | "slice" => "slices", 115 | "police" => "police" 116 | } 117 | 118 | CamelToUnderscore = { 119 | "Product" => "product", 120 | "SpecialGuest" => "special_guest", 121 | "ApplicationController" => "application_controller", 122 | "Area51Controller" => "area51_controller" 123 | } 124 | 125 | UnderscoreToLowerCamel = { 126 | "product" => "product", 127 | "special_guest" => "specialGuest", 128 | "application_controller" => "applicationController", 129 | "area51_controller" => "area51Controller" 130 | } 131 | 132 | SymbolToLowerCamel = { 133 | product: "product", 134 | special_guest: "specialGuest", 135 | application_controller: "applicationController", 136 | area51_controller: "area51Controller" 137 | } 138 | 139 | CamelToUnderscoreWithoutReverse = { 140 | "HTMLTidy" => "html_tidy", 141 | "HTMLTidyGenerator" => "html_tidy_generator", 142 | "FreeBSD" => "free_bsd", 143 | "HTML" => "html", 144 | "ForceXMLController" => "force_xml_controller", 145 | } 146 | 147 | CamelWithModuleToUnderscoreWithSlash = { 148 | "Admin::Product" => "admin/product", 149 | "Users::Commission::Department" => "users/commission/department", 150 | "UsersSection::CommissionDepartment" => "users_section/commission_department", 151 | } 152 | 153 | ClassNameToForeignKeyWithUnderscore = { 154 | "Person" => "person_id", 155 | "MyApplication::Billing::Account" => "account_id" 156 | } 157 | 158 | ClassNameToForeignKeyWithoutUnderscore = { 159 | "Person" => "personid", 160 | "MyApplication::Billing::Account" => "accountid" 161 | } 162 | 163 | ClassNameToTableName = { 164 | "PrimarySpokesman" => "primary_spokesmen", 165 | "NodeChild" => "node_children" 166 | } 167 | 168 | StringToParameterized = { 169 | "Donald E. Knuth" => "donald-e-knuth", 170 | "Random text with *(bad)* characters" => "random-text-with-bad-characters", 171 | "Allow_Under_Scores" => "allow_under_scores", 172 | "Trailing bad characters!@#" => "trailing-bad-characters", 173 | "!@#Leading bad characters" => "leading-bad-characters", 174 | "Squeeze separators" => "squeeze-separators", 175 | "Test with + sign" => "test-with-sign", 176 | # "Test with malformed utf8 \251" => "test-with-malformed-utf8" 177 | } 178 | 179 | StringToParameterizedPreserveCase = { 180 | "Donald E. Knuth" => "Donald-E-Knuth", 181 | "Random text with *(bad)* characters" => "Random-text-with-bad-characters", 182 | "Allow_Under_Scores" => "Allow_Under_Scores", 183 | "Trailing bad characters!@#" => "Trailing-bad-characters", 184 | "!@#Leading bad characters" => "Leading-bad-characters", 185 | "Squeeze separators" => "Squeeze-separators", 186 | "Test with + sign" => "Test-with-sign", 187 | # "Test with malformed utf8 \xA9" => "Test-with-malformed-utf8" 188 | } 189 | 190 | StringToParameterizeWithNoSeparator = { 191 | "Donald E. Knuth" => "donaldeknuth", 192 | "With-some-dashes" => "with-some-dashes", 193 | "Random text with *(bad)* characters" => "randomtextwithbadcharacters", 194 | "Trailing bad characters!@#" => "trailingbadcharacters", 195 | "!@#Leading bad characters" => "leadingbadcharacters", 196 | "Squeeze separators" => "squeezeseparators", 197 | "Test with + sign" => "testwithsign", 198 | # "Test with malformed utf8 \251" => "testwithmalformedutf8" 199 | } 200 | 201 | StringToParameterizePreserveCaseWithNoSeparator = { 202 | "Donald E. Knuth" => "DonaldEKnuth", 203 | "With-some-dashes" => "With-some-dashes", 204 | "Random text with *(bad)* characters" => "Randomtextwithbadcharacters", 205 | "Trailing bad characters!@#" => "Trailingbadcharacters", 206 | "!@#Leading bad characters" => "Leadingbadcharacters", 207 | "Squeeze separators" => "Squeezeseparators", 208 | "Test with + sign" => "Testwithsign", 209 | # "Test with malformed utf8 \xA9" => "Testwithmalformedutf8" 210 | } 211 | 212 | StringToParameterizeWithUnderscore = { 213 | "Donald E. Knuth" => "donald_e_knuth", 214 | "Random text with *(bad)* characters" => "random_text_with_bad_characters", 215 | "With-some-dashes" => "with-some-dashes", 216 | "Retain_underscore" => "retain_underscore", 217 | "Trailing bad characters!@#" => "trailing_bad_characters", 218 | "!@#Leading bad characters" => "leading_bad_characters", 219 | "Squeeze separators" => "squeeze_separators", 220 | "Test with + sign" => "test_with_sign", 221 | # "Test with malformed utf8 \251" => "test_with_malformed_utf8" 222 | } 223 | 224 | StringToParameterizePreserveCaseWithUnderscore = { 225 | "Donald E. Knuth" => "Donald_E_Knuth", 226 | "Random text with *(bad)* characters" => "Random_text_with_bad_characters", 227 | "With-some-dashes" => "With-some-dashes", 228 | "Allow_Under_Scores" => "Allow_Under_Scores", 229 | "Trailing bad characters!@#" => "Trailing_bad_characters", 230 | "!@#Leading bad characters" => "Leading_bad_characters", 231 | "Squeeze separators" => "Squeeze_separators", 232 | "Test with + sign" => "Test_with_sign", 233 | # "Test with malformed utf8 \xA9" => "Test_with_malformed_utf8" 234 | } 235 | 236 | StringToParameterizedAndNormalized = { 237 | "Malmö" => "malmo", 238 | "Garçons" => "garcons", 239 | # "Ops\331" => "opsu", 240 | "Ærøskøbing" => "aeroskobing", 241 | "Aßlar" => "asslar", 242 | "Japanese: 日本語" => "japanese" 243 | } 244 | 245 | UnderscoreToHuman = { 246 | "employee_salary" => "Employee salary", 247 | "employee_id" => "Employee", 248 | "underground" => "Underground", 249 | "_id" => "Id", 250 | "_external_id" => "External" 251 | } 252 | 253 | UnderscoreToHumanWithKeepIdSuffix = { 254 | "this_is_a_string_ending_with_id" => "This is a string ending with id", 255 | "employee_id" => "Employee id", 256 | "employee_id_something_else" => "Employee id something else", 257 | "underground" => "Underground", 258 | "_id" => "Id", 259 | "_external_id" => "External id" 260 | } 261 | 262 | UnderscoreToHumanWithoutCapitalize = { 263 | "employee_salary" => "employee salary", 264 | "employee_id" => "employee", 265 | "underground" => "underground" 266 | } 267 | 268 | MixtureToTitleCaseWithKeepIdSuffix = { 269 | "this_is_a_string_ending_with_id" => "This Is A String Ending With Id", 270 | "EmployeeId" => "Employee Id", 271 | "Author Id" => "Author Id" 272 | } 273 | 274 | MixtureToTitleCase = { 275 | "active_record" => "Active Record", 276 | "ActiveRecord" => "Active Record", 277 | "action web service" => "Action Web Service", 278 | "Action Web Service" => "Action Web Service", 279 | "Action web service" => "Action Web Service", 280 | "actionwebservice" => "Actionwebservice", 281 | "Actionwebservice" => "Actionwebservice", 282 | "david's code" => "David's Code", 283 | "David's code" => "David's Code", 284 | "david's Code" => "David's Code", 285 | "sgt. pepper's" => "Sgt. Pepper's", 286 | "i've just seen a face" => "I've Just Seen A Face", 287 | "maybe you'll be there" => "Maybe You'll Be There", 288 | "¿por qué?" => "¿Por Qué?", 289 | "Fred’s" => "Fred’s", 290 | "Fred`s" => "Fred`s", 291 | # "this was 'fake news'" => "This Was 'Fake News'", 292 | # ActiveSupport::SafeBuffer.new("confirmation num") => "Confirmation Num" 293 | } 294 | 295 | OrdinalNumbers = { 296 | "-1" => "-1st", 297 | "-2" => "-2nd", 298 | "-3" => "-3rd", 299 | "-4" => "-4th", 300 | "-5" => "-5th", 301 | "-6" => "-6th", 302 | "-7" => "-7th", 303 | "-8" => "-8th", 304 | "-9" => "-9th", 305 | "-10" => "-10th", 306 | "-11" => "-11th", 307 | "-12" => "-12th", 308 | "-13" => "-13th", 309 | "-14" => "-14th", 310 | "-20" => "-20th", 311 | "-21" => "-21st", 312 | "-22" => "-22nd", 313 | "-23" => "-23rd", 314 | "-24" => "-24th", 315 | "-100" => "-100th", 316 | "-101" => "-101st", 317 | "-102" => "-102nd", 318 | "-103" => "-103rd", 319 | "-104" => "-104th", 320 | "-110" => "-110th", 321 | "-111" => "-111th", 322 | "-112" => "-112th", 323 | "-113" => "-113th", 324 | "-1000" => "-1000th", 325 | "-1001" => "-1001st", 326 | "0" => "0th", 327 | "1" => "1st", 328 | "2" => "2nd", 329 | "3" => "3rd", 330 | "4" => "4th", 331 | "5" => "5th", 332 | "6" => "6th", 333 | "7" => "7th", 334 | "8" => "8th", 335 | "9" => "9th", 336 | "10" => "10th", 337 | "11" => "11th", 338 | "12" => "12th", 339 | "13" => "13th", 340 | "14" => "14th", 341 | "20" => "20th", 342 | "21" => "21st", 343 | "22" => "22nd", 344 | "23" => "23rd", 345 | "24" => "24th", 346 | "100" => "100th", 347 | "101" => "101st", 348 | "102" => "102nd", 349 | "103" => "103rd", 350 | "104" => "104th", 351 | "110" => "110th", 352 | "111" => "111th", 353 | "112" => "112th", 354 | "113" => "113th", 355 | "1000" => "1000th", 356 | "1001" => "1001st" 357 | } 358 | 359 | UnderscoresToDashes = { 360 | "street" => "street", 361 | "street_address" => "street-address", 362 | "person_street_address" => "person-street-address" 363 | } 364 | 365 | Irregularities = { 366 | "person" => "people", 367 | "man" => "men", 368 | "child" => "children", 369 | "sex" => "sexes", 370 | "move" => "moves", 371 | "cow" => "kine", # Test inflections with different starting letters 372 | "zombie" => "zombies", 373 | "genus" => "genera" 374 | } 375 | end 376 | -------------------------------------------------------------------------------- /test/core_ext/module_test.rb: -------------------------------------------------------------------------------- 1 | require 'abstract_unit' 2 | require 'active_support/core_ext/module' 3 | 4 | module One 5 | Constant1 = "Hello World" 6 | Constant2 = "What's up?" 7 | end 8 | 9 | class Ab 10 | include One 11 | Constant1 = "Hello World" # Will have different object id than One::Constant1 12 | Constant3 = "Goodbye World" 13 | end 14 | 15 | module Xy 16 | class Bc 17 | include One 18 | end 19 | end 20 | 21 | module Yz 22 | module Zy 23 | class Cd 24 | include One 25 | end 26 | end 27 | end 28 | 29 | Somewhere = Struct.new(:street, :city) do 30 | attr_accessor :name 31 | end 32 | 33 | class Someone < Struct.new(:name, :place) 34 | delegate :street, :city, :to_f, :to => :place 35 | delegate :name=, :to => :place, :prefix => true 36 | delegate :upcase, :to => "place.city" 37 | delegate :table_name, :to => :class 38 | delegate :table_name, :to => :class, :prefix => true 39 | 40 | def self.table_name 41 | 'some_table' 42 | end 43 | 44 | FAILED_DELEGATE_LINE = __LINE__ + 1 45 | delegate :foo, :to => :place 46 | 47 | FAILED_DELEGATE_LINE_2 = __LINE__ + 1 48 | delegate :bar, :to => :place, :allow_nil => true 49 | end 50 | 51 | Invoice = Struct.new(:client) do 52 | delegate :street, :city, :name, :to => :client, :prefix => true 53 | delegate :street, :city, :name, :to => :client, :prefix => :customer 54 | end 55 | 56 | Project = Struct.new(:description, :person) do 57 | delegate :name, :to => :person, :allow_nil => true 58 | delegate :to_f, :to => :description, :allow_nil => true 59 | end 60 | 61 | Developer = Struct.new(:client) do 62 | delegate :name, :to => :client, :prefix => nil 63 | end 64 | 65 | Tester = Struct.new(:client) do 66 | delegate :name, :to => :client, :prefix => false 67 | end 68 | 69 | class ParameterSet 70 | delegate :[], :[]=, :to => :@params 71 | 72 | def initialize 73 | @params = {:foo => "bar"} 74 | end 75 | end 76 | 77 | class Name 78 | delegate :upcase, :to => :@full_name 79 | 80 | def initialize(first, last) 81 | @full_name = "#{first} #{last}" 82 | end 83 | end 84 | 85 | class ModuleTest < ActiveSupport::TestCase 86 | def setup 87 | @david = Someone.new("David", Somewhere.new("Paulina", "Chicago")) 88 | end 89 | 90 | def test_delegation_to_methods 91 | assert_equal "Paulina", @david.street 92 | assert_equal "Chicago", @david.city 93 | end 94 | 95 | def test_delegation_to_assignment_method 96 | @david.place_name = "Fred" 97 | assert_equal "Fred", @david.place.name 98 | end 99 | 100 | def test_delegation_to_index_get_method 101 | @params = ParameterSet.new 102 | assert_equal "bar", @params[:foo] 103 | end 104 | 105 | def test_delegation_to_index_set_method 106 | @params = ParameterSet.new 107 | @params[:foo] = "baz" 108 | assert_equal "baz", @params[:foo] 109 | end 110 | 111 | # def test_delegation_down_hierarchy 112 | # assert_equal "CHICAGO", @david.upcase 113 | # end 114 | 115 | def test_delegation_to_instance_variable 116 | david = Name.new("David", "Hansson") 117 | assert_equal "DAVID HANSSON", david.upcase 118 | end 119 | 120 | def test_delegation_to_class_method 121 | assert_equal 'some_table', @david.table_name 122 | assert_equal 'some_table', @david.class_table_name 123 | end 124 | 125 | def test_missing_delegation_target 126 | assert_raise(ArgumentError) do 127 | Name.send :delegate, :nowhere 128 | end 129 | assert_raise(ArgumentError) do 130 | Name.send :delegate, :noplace, :tos => :hollywood 131 | end 132 | end 133 | 134 | def test_delegation_prefix 135 | invoice = Invoice.new(@david) 136 | assert_equal invoice.client_name, "David" 137 | assert_equal invoice.client_street, "Paulina" 138 | assert_equal invoice.client_city, "Chicago" 139 | end 140 | 141 | def test_delegation_custom_prefix 142 | invoice = Invoice.new(@david) 143 | assert_equal invoice.customer_name, "David" 144 | assert_equal invoice.customer_street, "Paulina" 145 | assert_equal invoice.customer_city, "Chicago" 146 | end 147 | 148 | def test_delegation_prefix_with_nil_or_false 149 | assert_equal Developer.new(@david).name, "David" 150 | assert_equal Tester.new(@david).name, "David" 151 | end 152 | 153 | def test_delegation_prefix_with_instance_variable 154 | assert_raise ArgumentError do 155 | Class.new do 156 | def initialize(client) 157 | @client = client 158 | end 159 | delegate :name, :address, :to => :@client, :prefix => true 160 | end 161 | end 162 | end 163 | 164 | def test_delegation_with_allow_nil 165 | rails = Project.new("Rails", Someone.new("David")) 166 | assert_equal rails.name, "David" 167 | end 168 | 169 | def test_delegation_with_allow_nil_and_nil_value 170 | rails = Project.new("Rails") 171 | assert_nil rails.name 172 | end 173 | 174 | def test_delegation_with_allow_nil_and_nil_value_and_prefix 175 | Project.class_eval do 176 | delegate :name, :to => :person, :allow_nil => true, :prefix => true 177 | end 178 | rails = Project.new("Rails") 179 | assert_nil rails.person_name 180 | end 181 | 182 | # def test_delegation_without_allow_nil_and_nil_value 183 | # david = Someone.new("David") 184 | # assert_raise(RuntimeError) { david.street } 185 | # end 186 | # 187 | # def test_delegation_to_method_that_exists_on_nil 188 | # nil_person = Someone.new(nil) 189 | # assert_equal 0.0, nil_person.to_f 190 | # end 191 | # 192 | # def test_delegation_to_method_that_exists_on_nil_when_allowing_nil 193 | # nil_project = Project.new(nil) 194 | # assert_equal 0.0, nil_project.to_f 195 | # end 196 | # 197 | # def test_delegation_does_not_raise_error_when_removing_singleton_instance_methods 198 | # parent = Class.new do 199 | # def self.parent_method; end 200 | # end 201 | # 202 | # assert_nothing_raised do 203 | # Class.new(parent) do 204 | # class << self 205 | # delegate :parent_method, :to => :superclass 206 | # end 207 | # end 208 | # end 209 | # end 210 | 211 | # DISABLED: Backtrace in JavaScript is Hard™ 212 | # def test_delegation_exception_backtrace 213 | # someone = Someone.new("foo", "bar") 214 | # someone.foo 215 | # rescue NoMethodError => e 216 | # file_and_line = "#{__FILE__}:#{Someone::FAILED_DELEGATE_LINE}" 217 | # # We can't simply check the first line of the backtrace, because JRuby reports the call to __send__ in the backtrace. 218 | # assert e.backtrace.any?{|a| a.include?(file_and_line)}, 219 | # "[#{e.backtrace.inspect}] did not include [#{file_and_line}]" 220 | # end 221 | 222 | # DISABLED: Backtrace in JavaScript is Hard™ 223 | # def test_delegation_exception_backtrace_with_allow_nil 224 | # someone = Someone.new("foo", "bar") 225 | # someone.bar 226 | # rescue NoMethodError => e 227 | # file_and_line = "#{__FILE__}:#{Someone::FAILED_DELEGATE_LINE_2}" 228 | # # We can't simply check the first line of the backtrace, because JRuby reports the call to __send__ in the backtrace. 229 | # assert e.backtrace.any?{|a| a.include?(file_and_line)}, 230 | # "[#{e.backtrace.inspect}] did not include [#{file_and_line}]" 231 | # end 232 | 233 | def test_parent 234 | assert_equal Yz::Zy, Yz::Zy::Cd.parent 235 | assert_equal Yz, Yz::Zy.parent 236 | assert_equal Object, Yz.parent 237 | end 238 | 239 | def test_parents 240 | assert_equal [Yz::Zy, Yz, Object], Yz::Zy::Cd.parents 241 | assert_equal [Yz, Object], Yz::Zy.parents 242 | end 243 | 244 | # def test_local_constants 245 | # assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s) 246 | # end 247 | # 248 | # def test_local_constant_names 249 | # ActiveSupport::Deprecation.silence do 250 | # assert_equal %w(Constant1 Constant3), Ab.local_constant_names.sort.map(&:to_s) 251 | # end 252 | # end 253 | end 254 | 255 | # module BarMethodAliaser 256 | # def self.included(foo_class) 257 | # foo_class.class_eval do 258 | # include BarMethods 259 | # alias_method_chain :bar, :baz 260 | # end 261 | # end 262 | # end 263 | # 264 | # module BarMethods 265 | # def bar_with_baz 266 | # bar_without_baz << '_with_baz' 267 | # end 268 | # 269 | # def quux_with_baz! 270 | # quux_without_baz! << '_with_baz' 271 | # end 272 | # 273 | # def quux_with_baz? 274 | # false 275 | # end 276 | # 277 | # def quux_with_baz=(v) 278 | # send(:quux_without_baz=, v) << '_with_baz' 279 | # end 280 | # 281 | # def duck_with_orange 282 | # duck_without_orange << '_with_orange' 283 | # end 284 | # end 285 | # 286 | # class MethodAliasingTest < ActiveSupport::TestCase 287 | # def setup 288 | # Object.const_set :FooClassWithBarMethod, Class.new { def bar() 'bar' end } 289 | # @instance = FooClassWithBarMethod.new 290 | # end 291 | # 292 | # def teardown 293 | # Object.instance_eval { remove_const :FooClassWithBarMethod } 294 | # end 295 | # 296 | # def test_alias_method_chain 297 | # assert @instance.respond_to?(:bar) 298 | # feature_aliases = [:bar_with_baz, :bar_without_baz] 299 | # 300 | # feature_aliases.each do |method| 301 | # assert !@instance.respond_to?(method) 302 | # end 303 | # 304 | # assert_equal 'bar', @instance.bar 305 | # 306 | # FooClassWithBarMethod.class_eval { include BarMethodAliaser } 307 | # 308 | # feature_aliases.each do |method| 309 | # assert_respond_to @instance, method 310 | # end 311 | # 312 | # assert_equal 'bar_with_baz', @instance.bar 313 | # assert_equal 'bar', @instance.bar_without_baz 314 | # end 315 | # 316 | # def test_alias_method_chain_with_punctuation_method 317 | # FooClassWithBarMethod.class_eval do 318 | # def quux!; 'quux' end 319 | # end 320 | # 321 | # assert !@instance.respond_to?(:quux_with_baz!) 322 | # FooClassWithBarMethod.class_eval do 323 | # include BarMethodAliaser 324 | # alias_method_chain :quux!, :baz 325 | # end 326 | # assert_respond_to @instance, :quux_with_baz! 327 | # 328 | # assert_equal 'quux_with_baz', @instance.quux! 329 | # assert_equal 'quux', @instance.quux_without_baz! 330 | # end 331 | # 332 | # def test_alias_method_chain_with_same_names_between_predicates_and_bang_methods 333 | # FooClassWithBarMethod.class_eval do 334 | # def quux!; 'quux!' end 335 | # def quux?; true end 336 | # def quux=(v); 'quux=' end 337 | # end 338 | # 339 | # assert !@instance.respond_to?(:quux_with_baz!) 340 | # assert !@instance.respond_to?(:quux_with_baz?) 341 | # assert !@instance.respond_to?(:quux_with_baz=) 342 | # 343 | # FooClassWithBarMethod.class_eval { include BarMethodAliaser } 344 | # assert_respond_to @instance, :quux_with_baz! 345 | # assert_respond_to @instance, :quux_with_baz? 346 | # assert_respond_to @instance, :quux_with_baz= 347 | # 348 | # 349 | # FooClassWithBarMethod.alias_method_chain :quux!, :baz 350 | # assert_equal 'quux!_with_baz', @instance.quux! 351 | # assert_equal 'quux!', @instance.quux_without_baz! 352 | # 353 | # FooClassWithBarMethod.alias_method_chain :quux?, :baz 354 | # assert_equal false, @instance.quux? 355 | # assert_equal true, @instance.quux_without_baz? 356 | # 357 | # FooClassWithBarMethod.alias_method_chain :quux=, :baz 358 | # assert_equal 'quux=_with_baz', @instance.send(:quux=, 1234) 359 | # assert_equal 'quux=', @instance.send(:quux_without_baz=, 1234) 360 | # end 361 | # 362 | # def test_alias_method_chain_with_feature_punctuation 363 | # FooClassWithBarMethod.class_eval do 364 | # def quux; 'quux' end 365 | # def quux?; 'quux?' end 366 | # include BarMethodAliaser 367 | # alias_method_chain :quux, :baz! 368 | # end 369 | # 370 | # assert_nothing_raised do 371 | # assert_equal 'quux_with_baz', @instance.quux_with_baz! 372 | # end 373 | # 374 | # assert_raise(NameError) do 375 | # FooClassWithBarMethod.alias_method_chain :quux?, :baz! 376 | # end 377 | # end 378 | # 379 | # def test_alias_method_chain_yields_target_and_punctuation 380 | # args = nil 381 | # 382 | # FooClassWithBarMethod.class_eval do 383 | # def quux?; end 384 | # include BarMethods 385 | # 386 | # FooClassWithBarMethod.alias_method_chain :quux?, :baz do |target, punctuation| 387 | # args = [target, punctuation] 388 | # end 389 | # end 390 | # 391 | # assert_not_nil args 392 | # assert_equal 'quux', args[0] 393 | # assert_equal '?', args[1] 394 | # end 395 | # 396 | # def test_alias_method_chain_preserves_private_method_status 397 | # FooClassWithBarMethod.class_eval do 398 | # def duck; 'duck' end 399 | # include BarMethodAliaser 400 | # private :duck 401 | # alias_method_chain :duck, :orange 402 | # end 403 | # 404 | # assert_raise NoMethodError do 405 | # @instance.duck 406 | # end 407 | # 408 | # assert_equal 'duck_with_orange', @instance.instance_eval { duck } 409 | # assert FooClassWithBarMethod.private_method_defined?(:duck) 410 | # end 411 | # 412 | # def test_alias_method_chain_preserves_protected_method_status 413 | # FooClassWithBarMethod.class_eval do 414 | # def duck; 'duck' end 415 | # include BarMethodAliaser 416 | # protected :duck 417 | # alias_method_chain :duck, :orange 418 | # end 419 | # 420 | # assert_raise NoMethodError do 421 | # @instance.duck 422 | # end 423 | # 424 | # assert_equal 'duck_with_orange', @instance.instance_eval { duck } 425 | # assert FooClassWithBarMethod.protected_method_defined?(:duck) 426 | # end 427 | # 428 | # def test_alias_method_chain_preserves_public_method_status 429 | # FooClassWithBarMethod.class_eval do 430 | # def duck; 'duck' end 431 | # include BarMethodAliaser 432 | # public :duck 433 | # alias_method_chain :duck, :orange 434 | # end 435 | # 436 | # assert_equal 'duck_with_orange', @instance.duck 437 | # assert FooClassWithBarMethod.public_method_defined?(:duck) 438 | # end 439 | # end 440 | -------------------------------------------------------------------------------- /test/core_ext/array_ext_test.rb: -------------------------------------------------------------------------------- 1 | require 'abstract_unit' 2 | require 'active_support/core_ext/array' 3 | # require 'active_support/core_ext/big_decimal' 4 | # require 'active_support/core_ext/object/conversions' 5 | 6 | require 'active_support/core_ext' # FIXME: pulling in all to_xml extensions 7 | require 'active_support/hash_with_indifferent_access' 8 | 9 | # class ArrayExtAccessTests < ActiveSupport::TestCase 10 | # def test_from 11 | # assert_equal %w( a b c d ), %w( a b c d ).from(0) 12 | # assert_equal %w( c d ), %w( a b c d ).from(2) 13 | # assert_equal %w(), %w( a b c d ).from(10) 14 | # end 15 | # 16 | # def test_to 17 | # assert_equal %w( a ), %w( a b c d ).to(0) 18 | # assert_equal %w( a b c ), %w( a b c d ).to(2) 19 | # assert_equal %w( a b c d ), %w( a b c d ).to(10) 20 | # end 21 | # 22 | # def test_second_through_tenth 23 | # array = (1..42).to_a 24 | # 25 | # assert_equal array[1], array.second 26 | # assert_equal array[2], array.third 27 | # assert_equal array[3], array.fourth 28 | # assert_equal array[4], array.fifth 29 | # assert_equal array[41], array.forty_two 30 | # end 31 | # end 32 | # 33 | # class ArrayExtToParamTests < ActiveSupport::TestCase 34 | # class ToParam < String 35 | # def to_param 36 | # "#{self}1" 37 | # end 38 | # end 39 | # 40 | # def test_string_array 41 | # assert_equal '', %w().to_param 42 | # assert_equal 'hello/world', %w(hello world).to_param 43 | # assert_equal 'hello/10', %w(hello 10).to_param 44 | # end 45 | # 46 | # def test_number_array 47 | # assert_equal '10/20', [10, 20].to_param 48 | # end 49 | # 50 | # def test_to_param_array 51 | # assert_equal 'custom1/param1', [ToParam.new('custom'), ToParam.new('param')].to_param 52 | # end 53 | # end 54 | # 55 | # class ArrayExtToSentenceTests < ActiveSupport::TestCase 56 | # def test_plain_array_to_sentence 57 | # assert_equal "", [].to_sentence 58 | # assert_equal "one", ['one'].to_sentence 59 | # assert_equal "one and two", ['one', 'two'].to_sentence 60 | # assert_equal "one, two, and three", ['one', 'two', 'three'].to_sentence 61 | # end 62 | # 63 | # def test_to_sentence_with_words_connector 64 | # assert_equal "one two, and three", ['one', 'two', 'three'].to_sentence(:words_connector => ' ') 65 | # assert_equal "one & two, and three", ['one', 'two', 'three'].to_sentence(:words_connector => ' & ') 66 | # assert_equal "onetwo, and three", ['one', 'two', 'three'].to_sentence(:words_connector => nil) 67 | # end 68 | # 69 | # def test_to_sentence_with_last_word_connector 70 | # assert_equal "one, two, and also three", ['one', 'two', 'three'].to_sentence(:last_word_connector => ', and also ') 71 | # assert_equal "one, twothree", ['one', 'two', 'three'].to_sentence(:last_word_connector => nil) 72 | # assert_equal "one, two three", ['one', 'two', 'three'].to_sentence(:last_word_connector => ' ') 73 | # assert_equal "one, two and three", ['one', 'two', 'three'].to_sentence(:last_word_connector => ' and ') 74 | # end 75 | # 76 | # def test_two_elements 77 | # assert_equal "one and two", ['one', 'two'].to_sentence 78 | # assert_equal "one two", ['one', 'two'].to_sentence(:two_words_connector => ' ') 79 | # end 80 | # 81 | # def test_one_element 82 | # assert_equal "one", ['one'].to_sentence 83 | # end 84 | # 85 | # def test_one_element_not_same_object 86 | # elements = ["one"] 87 | # assert_not_equal elements[0].object_id, elements.to_sentence.object_id 88 | # end 89 | # 90 | # def test_one_non_string_element 91 | # assert_equal '1', [1].to_sentence 92 | # end 93 | # 94 | # def test_does_not_modify_given_hash 95 | # options = { words_connector: ' ' } 96 | # assert_equal "one two, and three", ['one', 'two', 'three'].to_sentence(options) 97 | # assert_equal({ words_connector: ' ' }, options) 98 | # end 99 | # end 100 | # 101 | # class ArrayExtToSTests < ActiveSupport::TestCase 102 | # def test_to_s_db 103 | # collection = [ 104 | # Class.new { def id() 1 end }.new, 105 | # Class.new { def id() 2 end }.new, 106 | # Class.new { def id() 3 end }.new 107 | # ] 108 | # 109 | # assert_equal "null", [].to_s(:db) 110 | # assert_equal "1,2,3", collection.to_s(:db) 111 | # end 112 | # end 113 | 114 | class ArrayExtGroupingTests < ActiveSupport::TestCase 115 | def setup 116 | Fixnum.send :private, :/ # test we avoid Integer#/ (redefined by mathn) 117 | end 118 | 119 | def teardown 120 | Fixnum.send :public, :/ 121 | end 122 | 123 | def test_in_groups_of_with_perfect_fit 124 | groups = [] 125 | ('a'..'i').to_a.in_groups_of(3) do |group| 126 | groups << group 127 | end 128 | 129 | assert_equal [%w(a b c), %w(d e f), %w(g h i)], groups 130 | assert_equal [%w(a b c), %w(d e f), %w(g h i)], ('a'..'i').to_a.in_groups_of(3) 131 | end 132 | 133 | def test_in_groups_of_with_padding 134 | groups = [] 135 | ('a'..'g').to_a.in_groups_of(3) do |group| 136 | groups << group 137 | end 138 | 139 | assert_equal [%w(a b c), %w(d e f), ['g', nil, nil]], groups 140 | end 141 | 142 | def test_in_groups_of_pads_with_specified_values 143 | groups = [] 144 | 145 | ('a'..'g').to_a.in_groups_of(3, 'foo') do |group| 146 | groups << group 147 | end 148 | 149 | assert_equal [%w(a b c), %w(d e f), ['g', 'foo', 'foo']], groups 150 | end 151 | 152 | def test_in_groups_of_without_padding 153 | groups = [] 154 | 155 | ('a'..'g').to_a.in_groups_of(3, false) do |group| 156 | groups << group 157 | end 158 | 159 | assert_equal [%w(a b c), %w(d e f), ['g']], groups 160 | end 161 | 162 | def test_in_groups_returned_array_size 163 | array = (1..7).to_a 164 | 165 | 1.upto(array.size + 1) do |number| 166 | assert_equal number, array.in_groups(number).size 167 | end 168 | end 169 | 170 | def test_in_groups_with_empty_array 171 | assert_equal [[], [], []], [].in_groups(3) 172 | end 173 | 174 | def test_in_groups_with_block 175 | array = (1..9).to_a 176 | groups = [] 177 | 178 | array.in_groups(3) do |group| 179 | groups << group 180 | end 181 | 182 | assert_equal array.in_groups(3), groups 183 | end 184 | 185 | def test_in_groups_with_perfect_fit 186 | assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9]], 187 | (1..9).to_a.in_groups(3) 188 | end 189 | 190 | def test_in_groups_with_padding 191 | array = (1..7).to_a 192 | 193 | assert_equal [[1, 2, 3], [4, 5, nil], [6, 7, nil]], 194 | array.in_groups(3) 195 | assert_equal [[1, 2, 3], [4, 5, 'foo'], [6, 7, 'foo']], 196 | array.in_groups(3, 'foo') 197 | end 198 | 199 | def test_in_groups_without_padding 200 | assert_equal [[1, 2, 3], [4, 5], [6, 7]], 201 | (1..7).to_a.in_groups(3, false) 202 | end 203 | end 204 | 205 | class ArraySplitTests < ActiveSupport::TestCase 206 | def test_split_with_empty_array 207 | assert_equal [[]], [].split(0) 208 | end 209 | 210 | def test_split_with_argument 211 | assert_equal [[1, 2], [4, 5]], [1, 2, 3, 4, 5].split(3) 212 | assert_equal [[1, 2, 3, 4, 5]], [1, 2, 3, 4, 5].split(0) 213 | end 214 | 215 | def test_split_with_block 216 | assert_equal [[1, 2], [4, 5], [7, 8], [10]], (1..10).to_a.split { |i| i % 3 == 0 } 217 | end 218 | 219 | def test_split_with_edge_values 220 | assert_equal [[], [2, 3, 4, 5]], [1, 2, 3, 4, 5].split(1) 221 | assert_equal [[1, 2, 3, 4], []], [1, 2, 3, 4, 5].split(5) 222 | assert_equal [[], [2, 3, 4], []], [1, 2, 3, 4, 5].split { |i| i == 1 || i == 5 } 223 | end 224 | end 225 | 226 | # class ArrayToXmlTests < ActiveSupport::TestCase 227 | # def test_to_xml 228 | # xml = [ 229 | # { :name => "David", :age => 26, :age_in_millis => 820497600000 }, 230 | # { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') } 231 | # ].to_xml(:skip_instruct => true, :indent => 0) 232 | # 233 | # assert_equal '', xml.first(30) 234 | # assert xml.include?(%(26)), xml 235 | # assert xml.include?(%(820497600000)), xml 236 | # assert xml.include?(%(David)), xml 237 | # assert xml.include?(%(31)), xml 238 | # assert xml.include?(%(1.0)), xml 239 | # assert xml.include?(%(Jason)), xml 240 | # end 241 | # 242 | # def test_to_xml_with_dedicated_name 243 | # xml = [ 244 | # { :name => "David", :age => 26, :age_in_millis => 820497600000 }, { :name => "Jason", :age => 31 } 245 | # ].to_xml(:skip_instruct => true, :indent => 0, :root => "people") 246 | # 247 | # assert_equal '', xml.first(29) 248 | # end 249 | # 250 | # def test_to_xml_with_options 251 | # xml = [ 252 | # { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" } 253 | # ].to_xml(:skip_instruct => true, :skip_types => true, :indent => 0) 254 | # 255 | # assert_equal "", xml.first(17) 256 | # assert xml.include?(%(Paulina)) 257 | # assert xml.include?(%(David)) 258 | # assert xml.include?(%(Evergreen)) 259 | # assert xml.include?(%(Jason)) 260 | # end 261 | # 262 | # def test_to_xml_with_dasherize_false 263 | # xml = [ 264 | # { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" } 265 | # ].to_xml(:skip_instruct => true, :skip_types => true, :indent => 0, :dasherize => false) 266 | # 267 | # assert_equal "", xml.first(17) 268 | # assert xml.include?(%(Paulina)) 269 | # assert xml.include?(%(Evergreen)) 270 | # end 271 | # 272 | # def test_to_xml_with_dasherize_true 273 | # xml = [ 274 | # { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" } 275 | # ].to_xml(:skip_instruct => true, :skip_types => true, :indent => 0, :dasherize => true) 276 | # 277 | # assert_equal "", xml.first(17) 278 | # assert xml.include?(%(Paulina)) 279 | # assert xml.include?(%(Evergreen)) 280 | # end 281 | # 282 | # def test_to_with_instruct 283 | # xml = [ 284 | # { :name => "David", :age => 26, :age_in_millis => 820497600000 }, 285 | # { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') } 286 | # ].to_xml(:skip_instruct => false, :indent => 0) 287 | # 288 | # assert_match(/^<\?xml [^>]*/, xml) 289 | # assert_equal 0, xml.rindex(/<\?xml /) 290 | # end 291 | # 292 | # def test_to_xml_with_block 293 | # xml = [ 294 | # { :name => "David", :age => 26, :age_in_millis => 820497600000 }, 295 | # { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') } 296 | # ].to_xml(:skip_instruct => true, :indent => 0) do |builder| 297 | # builder.count 2 298 | # end 299 | # 300 | # assert xml.include?(%(2)), xml 301 | # end 302 | # 303 | # def test_to_xml_with_empty 304 | # xml = [].to_xml 305 | # assert_match(/type="array"\/>/, xml) 306 | # end 307 | # 308 | # def test_to_xml_dups_options 309 | # options = {:skip_instruct => true} 310 | # [].to_xml(options) 311 | # # :builder, etc, shouldn't be added to options 312 | # assert_equal({:skip_instruct => true}, options) 313 | # end 314 | # end 315 | 316 | class ArrayExtractOptionsTests < ActiveSupport::TestCase 317 | class HashSubclass < Hash 318 | end 319 | 320 | class ExtractableHashSubclass < Hash 321 | def extractable_options? 322 | true 323 | end 324 | end 325 | 326 | def test_extract_options 327 | assert_equal({}, [].extract_options!) 328 | assert_equal({}, [1].extract_options!) 329 | assert_equal({:a=>:b}, [{:a=>:b}].extract_options!) 330 | assert_equal({:a=>:b}, [1, {:a=>:b}].extract_options!) 331 | end 332 | 333 | def test_extract_options_doesnt_extract_hash_subclasses 334 | hash = HashSubclass.new 335 | hash[:foo] = 1 336 | array = [hash] 337 | options = array.extract_options! 338 | assert_equal({}, options) 339 | assert_equal [hash], array 340 | end 341 | 342 | def test_extract_options_extracts_extractable_subclass 343 | hash = ExtractableHashSubclass.new 344 | hash[:foo] = 1 345 | array = [hash] 346 | options = array.extract_options! 347 | assert_equal({:foo => 1}, options) 348 | assert_equal [], array 349 | end 350 | 351 | def test_extract_options_extracts_hwia 352 | hash = [{:foo => 1}.with_indifferent_access] 353 | options = hash.extract_options! 354 | assert_equal 1, options[:foo] 355 | end 356 | end 357 | 358 | # class ArrayUniqByTests < ActiveSupport::TestCase 359 | # def test_uniq_by 360 | # ActiveSupport::Deprecation.silence do 361 | # assert_equal [1,2], [1,2,3,4].uniq_by { |i| i.odd? } 362 | # assert_equal [1,2], [1,2,3,4].uniq_by(&:even?) 363 | # assert_equal((-5..0).to_a, (-5..5).to_a.uniq_by{ |i| i**2 }) 364 | # end 365 | # end 366 | # 367 | # def test_uniq_by! 368 | # a = [1,2,3,4] 369 | # ActiveSupport::Deprecation.silence do 370 | # a.uniq_by! { |i| i.odd? } 371 | # end 372 | # assert_equal [1,2], a 373 | # 374 | # a = [1,2,3,4] 375 | # ActiveSupport::Deprecation.silence do 376 | # a.uniq_by! { |i| i.even? } 377 | # end 378 | # assert_equal [1,2], a 379 | # 380 | # a = (-5..5).to_a 381 | # ActiveSupport::Deprecation.silence do 382 | # a.uniq_by! { |i| i**2 } 383 | # end 384 | # assert_equal((-5..0).to_a, a) 385 | # end 386 | # end 387 | 388 | class ArrayWrapperTests < ActiveSupport::TestCase 389 | class FakeCollection 390 | def to_ary 391 | ["foo", "bar"] 392 | end 393 | end 394 | 395 | class Proxy 396 | def initialize(target) @target = target end 397 | def method_missing(*a) @target.send(*a) end 398 | end 399 | 400 | class DoubtfulToAry 401 | def to_ary 402 | :not_an_array 403 | end 404 | end 405 | 406 | class NilToAry 407 | def to_ary 408 | nil 409 | end 410 | end 411 | 412 | def test_array 413 | ary = %w(foo bar) 414 | assert_same ary, Array.wrap(ary) 415 | end 416 | 417 | def test_nil 418 | assert_equal [], Array.wrap(nil) 419 | end 420 | 421 | def test_object 422 | o = Object.new 423 | assert_equal [o], Array.wrap(o) 424 | end 425 | 426 | def test_string 427 | assert_equal ["foo"], Array.wrap("foo") 428 | end 429 | 430 | def test_string_with_newline 431 | assert_equal ["foo\nbar"], Array.wrap("foo\nbar") 432 | end 433 | 434 | def test_object_with_to_ary 435 | assert_equal ["foo", "bar"], Array.wrap(FakeCollection.new) 436 | end 437 | 438 | def test_proxy_object 439 | p = Proxy.new(Object.new) 440 | assert_equal [p], Array.wrap(p) 441 | end 442 | 443 | def test_proxy_to_object_with_to_ary 444 | p = Proxy.new(FakeCollection.new) 445 | assert_equal [p], Array.wrap(p) 446 | end 447 | 448 | def test_struct 449 | o = Struct.new(:foo).new(123) 450 | assert_equal [o], Array.wrap(o) 451 | end 452 | 453 | def test_wrap_returns_wrapped_if_to_ary_returns_nil 454 | o = NilToAry.new 455 | assert_equal [o], Array.wrap(o) 456 | end 457 | 458 | def test_wrap_does_not_complain_if_to_ary_does_not_return_an_array 459 | assert_equal DoubtfulToAry.new.to_ary, Array.wrap(DoubtfulToAry.new) 460 | end 461 | end 462 | 463 | # class ArrayPrependAppendTest < ActiveSupport::TestCase 464 | # def test_append 465 | # assert_equal [1, 2], [1].append(2) 466 | # end 467 | # 468 | # def test_prepend 469 | # assert_equal [2, 1], [1].prepend(2) 470 | # end 471 | # end 472 | -------------------------------------------------------------------------------- /test/inflector_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "abstract_unit" 4 | require "active_support/inflector" 5 | 6 | require "inflector_test_cases" 7 | require "constantize_test_cases" 8 | 9 | class InflectorTest < ActiveSupport::TestCase 10 | include InflectorTestCases 11 | include ConstantizeTestCases 12 | 13 | def setup 14 | # Dups the singleton before each test, restoring the original inflections later. 15 | # 16 | # This helper is implemented by setting @__instance__ because in some tests 17 | # there are module functions that access ActiveSupport::Inflector.inflections, 18 | # so we need to replace the singleton itself. 19 | @original_inflections = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en] 20 | ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: @original_inflections.dup) 21 | end 22 | 23 | def teardown 24 | ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: @original_inflections) 25 | end 26 | 27 | def test_pluralize_plurals 28 | assert_equal "plurals", ActiveSupport::Inflector.pluralize("plurals") 29 | assert_equal "Plurals", ActiveSupport::Inflector.pluralize("Plurals") 30 | end 31 | 32 | def test_pluralize_empty_string 33 | assert_equal "", ActiveSupport::Inflector.pluralize("") 34 | end 35 | 36 | test "uncountability of ascii word" do 37 | word = "HTTP" 38 | ActiveSupport::Inflector.inflections do |inflect| 39 | inflect.uncountable word 40 | end 41 | 42 | assert_equal word, ActiveSupport::Inflector.pluralize(word) 43 | assert_equal word, ActiveSupport::Inflector.singularize(word) 44 | assert_equal ActiveSupport::Inflector.pluralize(word), ActiveSupport::Inflector.singularize(word) 45 | 46 | ActiveSupport::Inflector.inflections.uncountables.pop 47 | end 48 | 49 | test "uncountability of non-ascii word" do 50 | word = "猫" 51 | ActiveSupport::Inflector.inflections do |inflect| 52 | inflect.uncountable word 53 | end 54 | 55 | assert_equal word, ActiveSupport::Inflector.pluralize(word) 56 | assert_equal word, ActiveSupport::Inflector.singularize(word) 57 | assert_equal ActiveSupport::Inflector.pluralize(word), ActiveSupport::Inflector.singularize(word) 58 | 59 | ActiveSupport::Inflector.inflections.uncountables.pop 60 | end 61 | 62 | ActiveSupport::Inflector.inflections.uncountable.each do |word| 63 | define_method "test_uncountability_of_#{word}" do 64 | assert_equal word, ActiveSupport::Inflector.singularize(word) 65 | assert_equal word, ActiveSupport::Inflector.pluralize(word) 66 | assert_equal ActiveSupport::Inflector.pluralize(word), ActiveSupport::Inflector.singularize(word) 67 | end 68 | end 69 | 70 | def test_uncountable_word_is_not_greedy 71 | uncountable_word = "ors" 72 | countable_word = "sponsor" 73 | 74 | ActiveSupport::Inflector.inflections.uncountable << uncountable_word 75 | 76 | assert_equal uncountable_word, ActiveSupport::Inflector.singularize(uncountable_word) 77 | assert_equal uncountable_word, ActiveSupport::Inflector.pluralize(uncountable_word) 78 | assert_equal ActiveSupport::Inflector.pluralize(uncountable_word), ActiveSupport::Inflector.singularize(uncountable_word) 79 | 80 | assert_equal "sponsor", ActiveSupport::Inflector.singularize(countable_word) 81 | assert_equal "sponsors", ActiveSupport::Inflector.pluralize(countable_word) 82 | assert_equal "sponsor", ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.pluralize(countable_word)) 83 | end 84 | 85 | SingularToPlural.each do |singular, plural| 86 | define_method "test_pluralize_singular_#{singular}" do 87 | assert_equal(plural, ActiveSupport::Inflector.pluralize(singular)) 88 | assert_equal(plural.capitalize, ActiveSupport::Inflector.pluralize(singular.capitalize)) 89 | end 90 | end 91 | 92 | SingularToPlural.each do |singular, plural| 93 | define_method "test_singularize_plural_#{plural}" do 94 | assert_equal(singular, ActiveSupport::Inflector.singularize(plural)) 95 | assert_equal(singular.capitalize, ActiveSupport::Inflector.singularize(plural.capitalize)) 96 | end 97 | end 98 | 99 | SingularToPlural.each do |singular, plural| 100 | define_method "test_pluralize_plural_#{plural}" do 101 | assert_equal(plural, ActiveSupport::Inflector.pluralize(plural)) 102 | assert_equal(plural.capitalize, ActiveSupport::Inflector.pluralize(plural.capitalize)) 103 | end 104 | 105 | define_method "test_singularize_singular_#{singular}" do 106 | assert_equal(singular, ActiveSupport::Inflector.singularize(singular)) 107 | assert_equal(singular.capitalize, ActiveSupport::Inflector.singularize(singular.capitalize)) 108 | end 109 | end 110 | 111 | def test_overwrite_previous_inflectors 112 | assert_equal("series", ActiveSupport::Inflector.singularize("series")) 113 | ActiveSupport::Inflector.inflections.singular "series", "serie" 114 | assert_equal("serie", ActiveSupport::Inflector.singularize("series")) 115 | end 116 | 117 | MixtureToTitleCase.each_with_index do |(before, titleized), index| 118 | define_method "test_titleize_mixture_to_title_case_#{index}" do 119 | assert_equal(titleized, ActiveSupport::Inflector.titleize(before), "mixture \ 120 | to TitleCase failed for #{before}") 121 | end 122 | end 123 | 124 | MixtureToTitleCaseWithKeepIdSuffix.each_with_index do |(before, titleized), index| 125 | define_method "test_titleize_with_keep_id_suffix_mixture_to_title_case_#{index}" do 126 | assert_equal(titleized, ActiveSupport::Inflector.titleize(before, keep_id_suffix: true), 127 | "mixture to TitleCase with keep_id_suffix failed for #{before}") 128 | end 129 | end 130 | 131 | def test_camelize 132 | CamelToUnderscore.each do |camel, underscore| 133 | assert_equal(camel, ActiveSupport::Inflector.camelize(underscore)) 134 | end 135 | end 136 | 137 | # Broken: to pass this test we need a better regexps suport 138 | # def test_camelize_with_lower_downcases_the_first_letter 139 | # assert_equal("capital", ActiveSupport::Inflector.camelize("Capital", false)) 140 | # end 141 | 142 | def test_camelize_with_underscores 143 | assert_equal("CamelCase", ActiveSupport::Inflector.camelize("Camel_Case")) 144 | end 145 | 146 | # Broken: this method is not implemented 147 | # def test_acronyms 148 | # ActiveSupport::Inflector.inflections do |inflect| 149 | # inflect.acronym("API") 150 | # inflect.acronym("HTML") 151 | # inflect.acronym("HTTP") 152 | # inflect.acronym("RESTful") 153 | # inflect.acronym("W3C") 154 | # inflect.acronym("PhD") 155 | # inflect.acronym("RoR") 156 | # inflect.acronym("SSL") 157 | # end 158 | 159 | # # camelize underscore humanize titleize 160 | # [ 161 | # ["API", "api", "API", "API"], 162 | # ["APIController", "api_controller", "API controller", "API Controller"], 163 | # ["Nokogiri::HTML", "nokogiri/html", "Nokogiri/HTML", "Nokogiri/HTML"], 164 | # ["HTTPAPI", "http_api", "HTTP API", "HTTP API"], 165 | # ["HTTP::Get", "http/get", "HTTP/get", "HTTP/Get"], 166 | # ["SSLError", "ssl_error", "SSL error", "SSL Error"], 167 | # ["RESTful", "restful", "RESTful", "RESTful"], 168 | # ["RESTfulController", "restful_controller", "RESTful controller", "RESTful Controller"], 169 | # ["Nested::RESTful", "nested/restful", "Nested/RESTful", "Nested/RESTful"], 170 | # ["IHeartW3C", "i_heart_w3c", "I heart W3C", "I Heart W3C"], 171 | # ["PhDRequired", "phd_required", "PhD required", "PhD Required"], 172 | # ["IRoRU", "i_ror_u", "I RoR u", "I RoR U"], 173 | # ["RESTfulHTTPAPI", "restful_http_api", "RESTful HTTP API", "RESTful HTTP API"], 174 | # ["HTTP::RESTful", "http/restful", "HTTP/RESTful", "HTTP/RESTful"], 175 | # ["HTTP::RESTfulAPI", "http/restful_api", "HTTP/RESTful API", "HTTP/RESTful API"], 176 | # ["APIRESTful", "api_restful", "API RESTful", "API RESTful"], 177 | 178 | # # misdirection 179 | # ["Capistrano", "capistrano", "Capistrano", "Capistrano"], 180 | # ["CapiController", "capi_controller", "Capi controller", "Capi Controller"], 181 | # ["HttpsApis", "https_apis", "Https apis", "Https Apis"], 182 | # ["Html5", "html5", "Html5", "Html5"], 183 | # ["Restfully", "restfully", "Restfully", "Restfully"], 184 | # ["RoRails", "ro_rails", "Ro rails", "Ro Rails"] 185 | # ].each do |camel, under, human, title| 186 | # assert_equal(camel, ActiveSupport::Inflector.camelize(under)) 187 | # assert_equal(camel, ActiveSupport::Inflector.camelize(camel)) 188 | # assert_equal(under, ActiveSupport::Inflector.underscore(under)) 189 | # assert_equal(under, ActiveSupport::Inflector.underscore(camel)) 190 | # assert_equal(title, ActiveSupport::Inflector.titleize(under)) 191 | # assert_equal(title, ActiveSupport::Inflector.titleize(camel)) 192 | # assert_equal(human, ActiveSupport::Inflector.humanize(under)) 193 | # end 194 | # end 195 | 196 | # Broken: this method is not implemented 197 | # def test_acronym_override 198 | # ActiveSupport::Inflector.inflections do |inflect| 199 | # inflect.acronym("API") 200 | # inflect.acronym("LegacyApi") 201 | # end 202 | 203 | # assert_equal("LegacyApi", ActiveSupport::Inflector.camelize("legacyapi")) 204 | # assert_equal("LegacyAPI", ActiveSupport::Inflector.camelize("legacy_api")) 205 | # assert_equal("SomeLegacyApi", ActiveSupport::Inflector.camelize("some_legacyapi")) 206 | # assert_equal("Nonlegacyapi", ActiveSupport::Inflector.camelize("nonlegacyapi")) 207 | # end 208 | 209 | # Broken: this method is not implemented 210 | # def test_acronyms_camelize_lower 211 | # ActiveSupport::Inflector.inflections do |inflect| 212 | # inflect.acronym("API") 213 | # inflect.acronym("HTML") 214 | # end 215 | 216 | # assert_equal("htmlAPI", ActiveSupport::Inflector.camelize("html_api", false)) 217 | # assert_equal("htmlAPI", ActiveSupport::Inflector.camelize("htmlAPI", false)) 218 | # assert_equal("htmlAPI", ActiveSupport::Inflector.camelize("HTMLAPI", false)) 219 | # end 220 | 221 | # Broken: this method is not implemented 222 | # def test_underscore_acronym_sequence 223 | # ActiveSupport::Inflector.inflections do |inflect| 224 | # inflect.acronym("API") 225 | # inflect.acronym("JSON") 226 | # inflect.acronym("HTML") 227 | # end 228 | 229 | # assert_equal("json_html_api", ActiveSupport::Inflector.underscore("JSONHTMLAPI")) 230 | # end 231 | 232 | # Broken: this method is not implemented 233 | # def test_acronym_regexp_is_deprecated 234 | # assert_deprecated do 235 | # ActiveSupport::Inflector.inflections.acronym_regex 236 | # end 237 | # end 238 | 239 | def test_underscore 240 | CamelToUnderscore.each do |camel, underscore| 241 | assert_equal(underscore, ActiveSupport::Inflector.underscore(camel)) 242 | end 243 | CamelToUnderscoreWithoutReverse.each do |camel, underscore| 244 | assert_equal(underscore, ActiveSupport::Inflector.underscore(camel)) 245 | end 246 | end 247 | 248 | def test_camelize_with_module 249 | CamelWithModuleToUnderscoreWithSlash.each do |camel, underscore| 250 | assert_equal(camel, ActiveSupport::Inflector.camelize(underscore)) 251 | end 252 | end 253 | 254 | def test_underscore_with_slashes 255 | CamelWithModuleToUnderscoreWithSlash.each do |camel, underscore| 256 | assert_equal(underscore, ActiveSupport::Inflector.underscore(camel)) 257 | end 258 | end 259 | 260 | def test_demodulize 261 | assert_equal "Account", ActiveSupport::Inflector.demodulize("MyApplication::Billing::Account") 262 | assert_equal "Account", ActiveSupport::Inflector.demodulize("Account") 263 | assert_equal "Account", ActiveSupport::Inflector.demodulize("::Account") 264 | assert_equal "", ActiveSupport::Inflector.demodulize("") 265 | end 266 | 267 | def test_deconstantize 268 | assert_equal "MyApplication::Billing", ActiveSupport::Inflector.deconstantize("MyApplication::Billing::Account") 269 | assert_equal "::MyApplication::Billing", ActiveSupport::Inflector.deconstantize("::MyApplication::Billing::Account") 270 | 271 | assert_equal "MyApplication", ActiveSupport::Inflector.deconstantize("MyApplication::Billing") 272 | assert_equal "::MyApplication", ActiveSupport::Inflector.deconstantize("::MyApplication::Billing") 273 | 274 | assert_equal "", ActiveSupport::Inflector.deconstantize("Account") 275 | assert_equal "", ActiveSupport::Inflector.deconstantize("::Account") 276 | assert_equal "", ActiveSupport::Inflector.deconstantize("") 277 | end 278 | 279 | def test_foreign_key 280 | ClassNameToForeignKeyWithUnderscore.each do |klass, foreign_key| 281 | assert_equal(foreign_key, ActiveSupport::Inflector.foreign_key(klass)) 282 | end 283 | 284 | ClassNameToForeignKeyWithoutUnderscore.each do |klass, foreign_key| 285 | assert_equal(foreign_key, ActiveSupport::Inflector.foreign_key(klass, false)) 286 | end 287 | end 288 | 289 | def test_tableize 290 | ClassNameToTableName.each do |class_name, table_name| 291 | assert_equal(table_name, ActiveSupport::Inflector.tableize(class_name)) 292 | end 293 | end 294 | 295 | # Broken: this method is not implemented 296 | # def test_parameterize 297 | # StringToParameterized.each do |some_string, parameterized_string| 298 | # assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string)) 299 | # end 300 | # end 301 | 302 | # Broken: this method is not implemented 303 | # def test_parameterize_and_normalize 304 | # StringToParameterizedAndNormalized.each do |some_string, parameterized_string| 305 | # assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string)) 306 | # end 307 | # end 308 | 309 | # Broken: this method is not implemented 310 | # def test_parameterize_with_custom_separator 311 | # StringToParameterizeWithUnderscore.each do |some_string, parameterized_string| 312 | # assert_equal(parameterized_string, ActiveSupport::Inflector.parameterize(some_string, separator: "_")) 313 | # end 314 | # end 315 | 316 | # Broken: this method is not implemented 317 | # def test_parameterize_with_multi_character_separator 318 | # StringToParameterized.each do |some_string, parameterized_string| 319 | # assert_equal(parameterized_string.gsub("-", "__sep__"), ActiveSupport::Inflector.parameterize(some_string, separator: "__sep__")) 320 | # end 321 | # end 322 | 323 | def test_classify 324 | ClassNameToTableName.each do |class_name, table_name| 325 | assert_equal(class_name, ActiveSupport::Inflector.classify(table_name)) 326 | assert_equal(class_name, ActiveSupport::Inflector.classify("table_prefix." + table_name)) 327 | end 328 | end 329 | 330 | def test_classify_with_symbol 331 | assert_nothing_raised do 332 | assert_equal "FooBar", ActiveSupport::Inflector.classify(:foo_bars) 333 | end 334 | end 335 | 336 | def test_classify_with_leading_schema_name 337 | assert_equal "FooBar", ActiveSupport::Inflector.classify("schema.foo_bar") 338 | end 339 | 340 | def test_humanize 341 | UnderscoreToHuman.each do |underscore, human| 342 | assert_equal(human, ActiveSupport::Inflector.humanize(underscore)) 343 | end 344 | end 345 | 346 | def test_humanize_without_capitalize 347 | UnderscoreToHumanWithoutCapitalize.each do |underscore, human| 348 | assert_equal(human, ActiveSupport::Inflector.humanize(underscore, capitalize: false)) 349 | end 350 | end 351 | 352 | def test_humanize_with_keep_id_suffix 353 | UnderscoreToHumanWithKeepIdSuffix.each do |underscore, human| 354 | assert_equal(human, ActiveSupport::Inflector.humanize(underscore, keep_id_suffix: true)) 355 | end 356 | end 357 | 358 | def test_humanize_by_rule 359 | ActiveSupport::Inflector.inflections do |inflect| 360 | inflect.human(/_cnt$/i, '\1_count') 361 | inflect.human(/^prefx_/i, '\1') 362 | end 363 | assert_equal("Jargon count", ActiveSupport::Inflector.humanize("jargon_cnt")) 364 | assert_equal("Request", ActiveSupport::Inflector.humanize("prefx_request")) 365 | end 366 | 367 | def test_humanize_by_string 368 | ActiveSupport::Inflector.inflections do |inflect| 369 | inflect.human("col_rpted_bugs", "Reported bugs") 370 | end 371 | assert_equal("Reported bugs", ActiveSupport::Inflector.humanize("col_rpted_bugs")) 372 | assert_equal("Col rpted bugs", ActiveSupport::Inflector.humanize("COL_rpted_bugs")) 373 | end 374 | 375 | # Broken: .acronym is not implemented 376 | # def test_humanize_with_acronyms 377 | # ActiveSupport::Inflector.inflections do |inflect| 378 | # inflect.acronym "LAX" 379 | # inflect.acronym "SFO" 380 | # end 381 | # assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("LAX ROUNDTRIP TO SFO")) 382 | # assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("LAX ROUNDTRIP TO SFO", capitalize: false)) 383 | # assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("lax roundtrip to sfo")) 384 | # assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("lax roundtrip to sfo", capitalize: false)) 385 | # assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("Lax Roundtrip To Sfo")) 386 | # assert_equal("LAX roundtrip to SFO", ActiveSupport::Inflector.humanize("Lax Roundtrip To Sfo", capitalize: false)) 387 | # end 388 | 389 | def test_constantize 390 | run_constantize_tests_on do |string| 391 | ActiveSupport::Inflector.constantize(string) 392 | end 393 | end 394 | 395 | def test_safe_constantize 396 | run_safe_constantize_tests_on do |string| 397 | ActiveSupport::Inflector.safe_constantize(string) 398 | end 399 | end 400 | 401 | def test_ordinal 402 | OrdinalNumbers.each do |number, ordinalized| 403 | assert_equal(ordinalized, number + ActiveSupport::Inflector.ordinal(number)) 404 | end 405 | end 406 | 407 | def test_ordinalize 408 | OrdinalNumbers.each do |number, ordinalized| 409 | assert_equal(ordinalized, ActiveSupport::Inflector.ordinalize(number)) 410 | end 411 | end 412 | 413 | def test_dasherize 414 | UnderscoresToDashes.each do |underscored, dasherized| 415 | assert_equal(dasherized, ActiveSupport::Inflector.dasherize(underscored)) 416 | end 417 | end 418 | 419 | def test_underscore_as_reverse_of_dasherize 420 | UnderscoresToDashes.each_key do |underscored| 421 | assert_equal(underscored, ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.dasherize(underscored))) 422 | end 423 | end 424 | 425 | def test_underscore_to_lower_camel 426 | UnderscoreToLowerCamel.each do |underscored, lower_camel| 427 | assert_equal(lower_camel, ActiveSupport::Inflector.camelize(underscored, false)) 428 | end 429 | end 430 | 431 | def test_symbol_to_lower_camel 432 | SymbolToLowerCamel.each do |symbol, lower_camel| 433 | assert_equal(lower_camel, ActiveSupport::Inflector.camelize(symbol, false)) 434 | end 435 | end 436 | 437 | # %w{plurals singulars uncountables humans}.each do |inflection_type| 438 | # class_eval <<-RUBY, __FILE__, __LINE__ + 1 439 | # def test_clear_#{inflection_type} 440 | # ActiveSupport::Inflector.inflections.clear :#{inflection_type} 441 | # assert ActiveSupport::Inflector.inflections.#{inflection_type}.empty?, \"#{inflection_type} inflections should be empty after clear :#{inflection_type}\" 442 | # end 443 | # RUBY 444 | # end 445 | 446 | def test_inflector_locality 447 | ActiveSupport::Inflector.inflections(:es) do |inflect| 448 | inflect.plural(/$/, "s") 449 | inflect.plural(/z$/i, "ces") 450 | 451 | inflect.singular(/s$/, "") 452 | inflect.singular(/es$/, "") 453 | 454 | inflect.irregular("el", "los") 455 | 456 | inflect.uncountable("agua") 457 | end 458 | 459 | assert_equal("hijos", "hijo".pluralize(:es)) 460 | assert_equal("luces", "luz".pluralize(:es)) 461 | assert_equal("luzs", "luz".pluralize) 462 | 463 | assert_equal("sociedad", "sociedades".singularize(:es)) 464 | assert_equal("sociedade", "sociedades".singularize) 465 | 466 | assert_equal("los", "el".pluralize(:es)) 467 | assert_equal("els", "el".pluralize) 468 | 469 | assert_equal("agua", "agua".pluralize(:es)) 470 | assert_equal("aguas", "agua".pluralize) 471 | 472 | ActiveSupport::Inflector.inflections(:es) { |inflect| inflect.clear } 473 | 474 | assert_empty ActiveSupport::Inflector.inflections(:es).plurals 475 | assert_empty ActiveSupport::Inflector.inflections(:es).singulars 476 | assert_empty ActiveSupport::Inflector.inflections(:es).uncountables 477 | assert_not_empty ActiveSupport::Inflector.inflections.plurals 478 | assert_not_empty ActiveSupport::Inflector.inflections.singulars 479 | assert_not_empty ActiveSupport::Inflector.inflections.uncountables 480 | end 481 | 482 | def test_clear_all 483 | ActiveSupport::Inflector.inflections do |inflect| 484 | # ensure any data is present 485 | inflect.plural(/(quiz)$/i, '\1zes') 486 | inflect.singular(/(database)s$/i, '\1') 487 | inflect.uncountable("series") 488 | inflect.human("col_rpted_bugs", "Reported bugs") 489 | 490 | inflect.clear :all 491 | 492 | assert_empty inflect.plurals 493 | assert_empty inflect.singulars 494 | assert_empty inflect.uncountables 495 | assert_empty inflect.humans 496 | end 497 | end 498 | 499 | def test_clear_with_default 500 | ActiveSupport::Inflector.inflections do |inflect| 501 | # ensure any data is present 502 | inflect.plural(/(quiz)$/i, '\1zes') 503 | inflect.singular(/(database)s$/i, '\1') 504 | inflect.uncountable("series") 505 | inflect.human("col_rpted_bugs", "Reported bugs") 506 | 507 | inflect.clear 508 | 509 | assert_empty inflect.plurals 510 | assert_empty inflect.singulars 511 | assert_empty inflect.uncountables 512 | assert_empty inflect.humans 513 | end 514 | end 515 | 516 | Irregularities.each do |singular, plural| 517 | define_method("test_irregularity_between_#{singular}_and_#{plural}") do 518 | ActiveSupport::Inflector.inflections do |inflect| 519 | inflect.irregular(singular, plural) 520 | assert_equal singular, ActiveSupport::Inflector.singularize(plural) 521 | assert_equal plural, ActiveSupport::Inflector.pluralize(singular) 522 | end 523 | end 524 | end 525 | 526 | Irregularities.each do |singular, plural| 527 | define_method("test_pluralize_of_irregularity_#{plural}_should_be_the_same") do 528 | ActiveSupport::Inflector.inflections do |inflect| 529 | inflect.irregular(singular, plural) 530 | assert_equal plural, ActiveSupport::Inflector.pluralize(plural) 531 | end 532 | end 533 | end 534 | 535 | Irregularities.each do |singular, plural| 536 | define_method("test_singularize_of_irregularity_#{singular}_should_be_the_same") do 537 | ActiveSupport::Inflector.inflections do |inflect| 538 | inflect.irregular(singular, plural) 539 | assert_equal singular, ActiveSupport::Inflector.singularize(singular) 540 | end 541 | end 542 | end 543 | 544 | [ :all, [] ].each do |scope| 545 | ActiveSupport::Inflector.inflections do |inflect| 546 | define_method("test_clear_inflections_with_#{scope.kind_of?(Array) ? "no_arguments" : scope}") do 547 | # save all the inflections 548 | singulars, plurals, uncountables = inflect.singulars, inflect.plurals, inflect.uncountables 549 | 550 | # clear all the inflections 551 | inflect.clear(*scope) 552 | 553 | assert_equal [], inflect.singulars 554 | assert_equal [], inflect.plurals 555 | assert_equal [], inflect.uncountables 556 | 557 | # restore all the inflections 558 | singulars.reverse_each { |singular| inflect.singular(*singular) } 559 | plurals.reverse_each { |plural| inflect.plural(*plural) } 560 | inflect.uncountable(uncountables) 561 | 562 | assert_equal singulars, inflect.singulars 563 | assert_equal plurals, inflect.plurals 564 | assert_equal uncountables, inflect.uncountables 565 | end 566 | end 567 | end 568 | 569 | %w(plurals singulars uncountables humans).each do |scope| 570 | define_method("test_clear_inflections_with_#{scope}") do 571 | # clear the inflections 572 | ActiveSupport::Inflector.inflections do |inflect| 573 | inflect.clear(scope) 574 | assert_equal [], inflect.send(scope) 575 | end 576 | end 577 | end 578 | end 579 | -------------------------------------------------------------------------------- /test/core_ext/numeric_ext_test.rb: -------------------------------------------------------------------------------- 1 | require 'abstract_unit' 2 | require 'active_support/time' 3 | require 'active_support/core_ext/numeric' 4 | require 'active_support/core_ext/integer' 5 | 6 | class DateTime < Date 7 | end unless defined? DateTime 8 | 9 | class NumericExtTimeAndDateTimeTest < ActiveSupport::TestCase 10 | def setup 11 | @now = Time.local(2005,2,10,15,30,45) 12 | # @dtnow = DateTime.civil(2005,2,10,15,30,45) 13 | @seconds = { 14 | 1.minute => 60, 15 | 10.minutes => 600, 16 | 1.hour + 15.minutes => 4500, 17 | 2.days + 4.hours + 30.minutes => 189000, 18 | 5.years + 1.month + 1.fortnight => 161589600 19 | } 20 | end 21 | 22 | def test_units 23 | @seconds.each do |actual, expected| 24 | assert_equal expected, actual 25 | end 26 | end 27 | 28 | def test_intervals 29 | @seconds.values.each do |seconds| 30 | assert_equal seconds.since(@now), @now + seconds 31 | assert_equal seconds.until(@now), @now - seconds 32 | end 33 | end 34 | 35 | # # Test intervals based from Time.now 36 | # def test_now 37 | # @seconds.values.each do |seconds| 38 | # now = Time.now 39 | # assert seconds.ago >= now - seconds 40 | # now = Time.now 41 | # assert seconds.from_now >= now + seconds 42 | # end 43 | # end 44 | # 45 | # def test_irregular_durations 46 | # assert_equal @now.advance(:days => 3000), 3000.days.since(@now) 47 | # assert_equal @now.advance(:months => 1), 1.month.since(@now) 48 | # assert_equal @now.advance(:months => -1), 1.month.until(@now) 49 | # assert_equal @now.advance(:years => 20), 20.years.since(@now) 50 | # assert_equal @dtnow.advance(:days => 3000), 3000.days.since(@dtnow) 51 | # assert_equal @dtnow.advance(:months => 1), 1.month.since(@dtnow) 52 | # assert_equal @dtnow.advance(:months => -1), 1.month.until(@dtnow) 53 | # assert_equal @dtnow.advance(:years => 20), 20.years.since(@dtnow) 54 | # end 55 | # 56 | # def test_duration_addition 57 | # assert_equal @now.advance(:days => 1).advance(:months => 1), (1.day + 1.month).since(@now) 58 | # assert_equal @now.advance(:days => 7), (1.week + 5.seconds - 5.seconds).since(@now) 59 | # assert_equal @now.advance(:years => 2), (4.years - 2.years).since(@now) 60 | # assert_equal @dtnow.advance(:days => 1).advance(:months => 1), (1.day + 1.month).since(@dtnow) 61 | # assert_equal @dtnow.advance(:days => 7), (1.week + 5.seconds - 5.seconds).since(@dtnow) 62 | # assert_equal @dtnow.advance(:years => 2), (4.years - 2.years).since(@dtnow) 63 | # end 64 | # 65 | # def test_time_plus_duration 66 | # assert_equal @now + 8, @now + 8.seconds 67 | # assert_equal @now + 22.9, @now + 22.9.seconds 68 | # assert_equal @now.advance(:days => 15), @now + 15.days 69 | # assert_equal @now.advance(:months => 1), @now + 1.month 70 | # assert_equal @dtnow.since(8), @dtnow + 8.seconds 71 | # assert_equal @dtnow.since(22.9), @dtnow + 22.9.seconds 72 | # assert_equal @dtnow.advance(:days => 15), @dtnow + 15.days 73 | # assert_equal @dtnow.advance(:months => 1), @dtnow + 1.month 74 | # end 75 | # 76 | # def test_chaining_duration_operations 77 | # assert_equal @now.advance(:days => 2).advance(:months => -3), @now + 2.days - 3.months 78 | # assert_equal @now.advance(:days => 1).advance(:months => 2), @now + 1.day + 2.months 79 | # assert_equal @dtnow.advance(:days => 2).advance(:months => -3), @dtnow + 2.days - 3.months 80 | # assert_equal @dtnow.advance(:days => 1).advance(:months => 2), @dtnow + 1.day + 2.months 81 | # end 82 | # 83 | # def test_duration_after_convertion_is_no_longer_accurate 84 | # assert_equal 30.days.to_i.since(@now), 1.month.to_i.since(@now) 85 | # assert_equal 365.25.days.to_f.since(@now), 1.year.to_f.since(@now) 86 | # assert_equal 30.days.to_i.since(@dtnow), 1.month.to_i.since(@dtnow) 87 | # assert_equal 365.25.days.to_f.since(@dtnow), 1.year.to_f.since(@dtnow) 88 | # end 89 | # 90 | # def test_add_one_year_to_leap_day 91 | # assert_equal Time.utc(2005,2,28,15,15,10), Time.utc(2004,2,29,15,15,10) + 1.year 92 | # assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10) + 1.year 93 | # end 94 | # 95 | # def test_since_and_ago_anchored_to_time_now_when_time_zone_is_not_set 96 | # Time.zone = nil 97 | # with_env_tz 'US/Eastern' do 98 | # Time.stubs(:now).returns Time.local(2000) 99 | # # since 100 | # assert_equal false, 5.since.is_a?(ActiveSupport::TimeWithZone) 101 | # assert_equal Time.local(2000,1,1,0,0,5), 5.since 102 | # # ago 103 | # assert_equal false, 5.ago.is_a?(ActiveSupport::TimeWithZone) 104 | # assert_equal Time.local(1999,12,31,23,59,55), 5.ago 105 | # end 106 | # end 107 | # 108 | # def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_is_set 109 | # Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] 110 | # with_env_tz 'US/Eastern' do 111 | # Time.stubs(:now).returns Time.local(2000) 112 | # # since 113 | # assert_equal true, 5.since.is_a?(ActiveSupport::TimeWithZone) 114 | # assert_equal Time.utc(2000,1,1,0,0,5), 5.since.time 115 | # assert_equal 'Eastern Time (US & Canada)', 5.since.time_zone.name 116 | # # ago 117 | # assert_equal true, 5.ago.is_a?(ActiveSupport::TimeWithZone) 118 | # assert_equal Time.utc(1999,12,31,23,59,55), 5.ago.time 119 | # assert_equal 'Eastern Time (US & Canada)', 5.ago.time_zone.name 120 | # end 121 | # ensure 122 | # Time.zone = nil 123 | # end 124 | # 125 | # protected 126 | # def with_env_tz(new_tz = 'US/Eastern') 127 | # old_tz, ENV['TZ'] = ENV['TZ'], new_tz 128 | # yield 129 | # ensure 130 | # old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') 131 | # end 132 | end 133 | 134 | # class NumericExtDateTest < ActiveSupport::TestCase 135 | # def setup 136 | # @today = Date.today 137 | # end 138 | # 139 | # def test_date_plus_duration 140 | # assert_equal @today + 1, @today + 1.day 141 | # assert_equal @today >> 1, @today + 1.month 142 | # assert_equal @today.to_time.since(1), @today + 1.second 143 | # assert_equal @today.to_time.since(60), @today + 1.minute 144 | # assert_equal @today.to_time.since(60*60), @today + 1.hour 145 | # end 146 | # 147 | # def test_chaining_duration_operations 148 | # assert_equal @today.advance(:days => 2).advance(:months => -3), @today + 2.days - 3.months 149 | # assert_equal @today.advance(:days => 1).advance(:months => 2), @today + 1.day + 2.months 150 | # end 151 | # 152 | # def test_add_one_year_to_leap_day 153 | # assert_equal Date.new(2005,2,28), Date.new(2004,2,29) + 1.year 154 | # end 155 | # end 156 | # 157 | # class NumericExtSizeTest < ActiveSupport::TestCase 158 | # def test_unit_in_terms_of_another 159 | # relationships = { 160 | # 1024.bytes => 1.kilobyte, 161 | # 1024.kilobytes => 1.megabyte, 162 | # 3584.0.kilobytes => 3.5.megabytes, 163 | # 3584.0.megabytes => 3.5.gigabytes, 164 | # 1.kilobyte ** 4 => 1.terabyte, 165 | # 1024.kilobytes + 2.megabytes => 3.megabytes, 166 | # 2.gigabytes / 4 => 512.megabytes, 167 | # 256.megabytes * 20 + 5.gigabytes => 10.gigabytes, 168 | # 1.kilobyte ** 5 => 1.petabyte, 169 | # 1.kilobyte ** 6 => 1.exabyte 170 | # } 171 | # 172 | # relationships.each do |left, right| 173 | # assert_equal right, left 174 | # end 175 | # end 176 | # 177 | # def test_units_as_bytes_independently 178 | # assert_equal 3145728, 3.megabytes 179 | # assert_equal 3145728, 3.megabyte 180 | # assert_equal 3072, 3.kilobytes 181 | # assert_equal 3072, 3.kilobyte 182 | # assert_equal 3221225472, 3.gigabytes 183 | # assert_equal 3221225472, 3.gigabyte 184 | # assert_equal 3298534883328, 3.terabytes 185 | # assert_equal 3298534883328, 3.terabyte 186 | # assert_equal 3377699720527872, 3.petabytes 187 | # assert_equal 3377699720527872, 3.petabyte 188 | # assert_equal 3458764513820540928, 3.exabytes 189 | # assert_equal 3458764513820540928, 3.exabyte 190 | # end 191 | # end 192 | # 193 | # class NumericExtFormattingTest < ActiveSupport::TestCase 194 | # def kilobytes(number) 195 | # number * 1024 196 | # end 197 | # 198 | # def megabytes(number) 199 | # kilobytes(number) * 1024 200 | # end 201 | # 202 | # def gigabytes(number) 203 | # megabytes(number) * 1024 204 | # end 205 | # 206 | # def terabytes(number) 207 | # gigabytes(number) * 1024 208 | # end 209 | # 210 | # def test_to_s__phone 211 | # assert_equal("555-1234", 5551234.to_s(:phone)) 212 | # assert_equal("800-555-1212", 8005551212.to_s(:phone)) 213 | # assert_equal("(800) 555-1212", 8005551212.to_s(:phone, :area_code => true)) 214 | # assert_equal("800 555 1212", 8005551212.to_s(:phone, :delimiter => " ")) 215 | # assert_equal("(800) 555-1212 x 123", 8005551212.to_s(:phone, :area_code => true, :extension => 123)) 216 | # assert_equal("800-555-1212", 8005551212.to_s(:phone, :extension => " ")) 217 | # assert_equal("555.1212", 5551212.to_s(:phone, :delimiter => '.')) 218 | # assert_equal("+1-800-555-1212", 8005551212.to_s(:phone, :country_code => 1)) 219 | # assert_equal("+18005551212", 8005551212.to_s(:phone, :country_code => 1, :delimiter => '')) 220 | # assert_equal("22-555-1212", 225551212.to_s(:phone)) 221 | # assert_equal("+45-22-555-1212", 225551212.to_s(:phone, :country_code => 45)) 222 | # end 223 | # 224 | # def test_to_s__currency 225 | # assert_equal("$1,234,567,890.50", 1234567890.50.to_s(:currency)) 226 | # assert_equal("$1,234,567,890.51", 1234567890.506.to_s(:currency)) 227 | # assert_equal("-$1,234,567,890.50", -1234567890.50.to_s(:currency)) 228 | # assert_equal("-$ 1,234,567,890.50", -1234567890.50.to_s(:currency, :format => "%u %n")) 229 | # assert_equal("($1,234,567,890.50)", -1234567890.50.to_s(:currency, :negative_format => "(%u%n)")) 230 | # assert_equal("$1,234,567,892", 1234567891.50.to_s(:currency, :precision => 0)) 231 | # assert_equal("$1,234,567,890.5", 1234567890.50.to_s(:currency, :precision => 1)) 232 | # assert_equal("£1234567890,50", 1234567890.50.to_s(:currency, :unit => "£", :separator => ",", :delimiter => "")) 233 | # end 234 | # 235 | # 236 | # def test_to_s__rounded 237 | # assert_equal("-111.235", -111.2346.to_s(:rounded)) 238 | # assert_equal("111.235", 111.2346.to_s(:rounded)) 239 | # assert_equal("31.83", 31.825.to_s(:rounded, :precision => 2)) 240 | # assert_equal("111.23", 111.2346.to_s(:rounded, :precision => 2)) 241 | # assert_equal("111.00", 111.to_s(:rounded, :precision => 2)) 242 | # assert_equal("3268", (32.6751 * 100.00).to_s(:rounded, :precision => 0)) 243 | # assert_equal("112", 111.50.to_s(:rounded, :precision => 0)) 244 | # assert_equal("1234567892", 1234567891.50.to_s(:rounded, :precision => 0)) 245 | # assert_equal("0", 0.to_s(:rounded, :precision => 0)) 246 | # assert_equal("0.00100", 0.001.to_s(:rounded, :precision => 5)) 247 | # assert_equal("0.001", 0.00111.to_s(:rounded, :precision => 3)) 248 | # assert_equal("10.00", 9.995.to_s(:rounded, :precision => 2)) 249 | # assert_equal("11.00", 10.995.to_s(:rounded, :precision => 2)) 250 | # assert_equal("0.00", -0.001.to_s(:rounded, :precision => 2)) 251 | # end 252 | # 253 | # def test_to_s__percentage 254 | # assert_equal("100.000%", 100.to_s(:percentage)) 255 | # assert_equal("100%", 100.to_s(:percentage, :precision => 0)) 256 | # assert_equal("302.06%", 302.0574.to_s(:percentage, :precision => 2)) 257 | # assert_equal("123.4%", 123.400.to_s(:percentage, :precision => 3, :strip_insignificant_zeros => true)) 258 | # assert_equal("1.000,000%", 1000.to_s(:percentage, :delimiter => '.', :separator => ',')) 259 | # assert_equal("1000.000 %", 1000.to_s(:percentage, :format => "%n %")) 260 | # end 261 | # 262 | # def test_to_s__delimited 263 | # assert_equal("12,345,678", 12345678.to_s(:delimited)) 264 | # assert_equal("0", 0.to_s(:delimited)) 265 | # assert_equal("123", 123.to_s(:delimited)) 266 | # assert_equal("123,456", 123456.to_s(:delimited)) 267 | # assert_equal("123,456.78", 123456.78.to_s(:delimited)) 268 | # assert_equal("123,456.789", 123456.789.to_s(:delimited)) 269 | # assert_equal("123,456.78901", 123456.78901.to_s(:delimited)) 270 | # assert_equal("123,456,789.78901", 123456789.78901.to_s(:delimited)) 271 | # assert_equal("0.78901", 0.78901.to_s(:delimited)) 272 | # end 273 | # 274 | # def test_to_s__delimited__with_options_hash 275 | # assert_equal '12 345 678', 12345678.to_s(:delimited, :delimiter => ' ') 276 | # assert_equal '12,345,678-05', 12345678.05.to_s(:delimited, :separator => '-') 277 | # assert_equal '12.345.678,05', 12345678.05.to_s(:delimited, :separator => ',', :delimiter => '.') 278 | # assert_equal '12.345.678,05', 12345678.05.to_s(:delimited, :delimiter => '.', :separator => ',') 279 | # end 280 | # 281 | # 282 | # def test_to_s__rounded_with_custom_delimiter_and_separator 283 | # assert_equal '31,83', 31.825.to_s(:rounded, :precision => 2, :separator => ',') 284 | # assert_equal '1.231,83', 1231.825.to_s(:rounded, :precision => 2, :separator => ',', :delimiter => '.') 285 | # end 286 | # 287 | # def test_to_s__rounded__with_significant_digits 288 | # assert_equal "124000", 123987.to_s(:rounded, :precision => 3, :significant => true) 289 | # assert_equal "120000000", 123987876.to_s(:rounded, :precision => 2, :significant => true ) 290 | # assert_equal "9775", 9775.to_s(:rounded, :precision => 4, :significant => true ) 291 | # assert_equal "5.4", 5.3923.to_s(:rounded, :precision => 2, :significant => true ) 292 | # assert_equal "5", 5.3923.to_s(:rounded, :precision => 1, :significant => true ) 293 | # assert_equal "1", 1.232.to_s(:rounded, :precision => 1, :significant => true ) 294 | # assert_equal "7", 7.to_s(:rounded, :precision => 1, :significant => true ) 295 | # assert_equal "1", 1.to_s(:rounded, :precision => 1, :significant => true ) 296 | # assert_equal "53", 52.7923.to_s(:rounded, :precision => 2, :significant => true ) 297 | # assert_equal "9775.00", 9775.to_s(:rounded, :precision => 6, :significant => true ) 298 | # assert_equal "5.392900", 5.3929.to_s(:rounded, :precision => 7, :significant => true ) 299 | # assert_equal "0.0", 0.to_s(:rounded, :precision => 2, :significant => true ) 300 | # assert_equal "0", 0.to_s(:rounded, :precision => 1, :significant => true ) 301 | # assert_equal "0.0001", 0.0001.to_s(:rounded, :precision => 1, :significant => true ) 302 | # assert_equal "0.000100", 0.0001.to_s(:rounded, :precision => 3, :significant => true ) 303 | # assert_equal "0.0001", 0.0001111.to_s(:rounded, :precision => 1, :significant => true ) 304 | # assert_equal "10.0", 9.995.to_s(:rounded, :precision => 3, :significant => true) 305 | # assert_equal "9.99", 9.994.to_s(:rounded, :precision => 3, :significant => true) 306 | # assert_equal "11.0", 10.995.to_s(:rounded, :precision => 3, :significant => true) 307 | # end 308 | # 309 | # def test_to_s__rounded__with_strip_insignificant_zeros 310 | # assert_equal "9775.43", 9775.43.to_s(:rounded, :precision => 4, :strip_insignificant_zeros => true ) 311 | # assert_equal "9775.2", 9775.2.to_s(:rounded, :precision => 6, :significant => true, :strip_insignificant_zeros => true ) 312 | # assert_equal "0", 0.to_s(:rounded, :precision => 6, :significant => true, :strip_insignificant_zeros => true ) 313 | # end 314 | # 315 | # def test_to_s__rounded__with_significant_true_and_zero_precision 316 | # # Zero precision with significant is a mistake (would always return zero), 317 | # # so we treat it as if significant was false (increases backwards compatibility for number_to_human_size) 318 | # assert_equal "124", 123.987.to_s(:rounded, :precision => 0, :significant => true) 319 | # assert_equal "12", 12.to_s(:rounded, :precision => 0, :significant => true ) 320 | # end 321 | # 322 | # def test_to_s__human_size 323 | # assert_equal '0 Bytes', 0.to_s(:human_size) 324 | # assert_equal '1 Byte', 1.to_s(:human_size) 325 | # assert_equal '3 Bytes', 3.14159265.to_s(:human_size) 326 | # assert_equal '123 Bytes', 123.0.to_s(:human_size) 327 | # assert_equal '123 Bytes', 123.to_s(:human_size) 328 | # assert_equal '1.21 KB', 1234.to_s(:human_size) 329 | # assert_equal '12.1 KB', 12345.to_s(:human_size) 330 | # assert_equal '1.18 MB', 1234567.to_s(:human_size) 331 | # assert_equal '1.15 GB', 1234567890.to_s(:human_size) 332 | # assert_equal '1.12 TB', 1234567890123.to_s(:human_size) 333 | # assert_equal '1030 TB', terabytes(1026).to_s(:human_size) 334 | # assert_equal '444 KB', kilobytes(444).to_s(:human_size) 335 | # assert_equal '1020 MB', megabytes(1023).to_s(:human_size) 336 | # assert_equal '3 TB', terabytes(3).to_s(:human_size) 337 | # assert_equal '1.2 MB', 1234567.to_s(:human_size, :precision => 2) 338 | # assert_equal '3 Bytes', 3.14159265.to_s(:human_size, :precision => 4) 339 | # assert_equal '1 KB', kilobytes(1.0123).to_s(:human_size, :precision => 2) 340 | # assert_equal '1.01 KB', kilobytes(1.0100).to_s(:human_size, :precision => 4) 341 | # assert_equal '10 KB', kilobytes(10.000).to_s(:human_size, :precision => 4) 342 | # assert_equal '1 Byte', 1.1.to_s(:human_size) 343 | # assert_equal '10 Bytes', 10.to_s(:human_size) 344 | # end 345 | # 346 | # def test_to_s__human_size_with_si_prefix 347 | # assert_equal '3 Bytes', 3.14159265.to_s(:human_size, :prefix => :si) 348 | # assert_equal '123 Bytes', 123.0.to_s(:human_size, :prefix => :si) 349 | # assert_equal '123 Bytes', 123.to_s(:human_size, :prefix => :si) 350 | # assert_equal '1.23 KB', 1234.to_s(:human_size, :prefix => :si) 351 | # assert_equal '12.3 KB', 12345.to_s(:human_size, :prefix => :si) 352 | # assert_equal '1.23 MB', 1234567.to_s(:human_size, :prefix => :si) 353 | # assert_equal '1.23 GB', 1234567890.to_s(:human_size, :prefix => :si) 354 | # assert_equal '1.23 TB', 1234567890123.to_s(:human_size, :prefix => :si) 355 | # end 356 | # 357 | # def test_to_s__human_size_with_options_hash 358 | # assert_equal '1.2 MB', 1234567.to_s(:human_size, :precision => 2) 359 | # assert_equal '3 Bytes', 3.14159265.to_s(:human_size, :precision => 4) 360 | # assert_equal '1 KB', kilobytes(1.0123).to_s(:human_size, :precision => 2) 361 | # assert_equal '1.01 KB', kilobytes(1.0100).to_s(:human_size, :precision => 4) 362 | # assert_equal '10 KB', kilobytes(10.000).to_s(:human_size, :precision => 4) 363 | # assert_equal '1 TB', 1234567890123.to_s(:human_size, :precision => 1) 364 | # assert_equal '500 MB', 524288000.to_s(:human_size, :precision=>3) 365 | # assert_equal '10 MB', 9961472.to_s(:human_size, :precision=>0) 366 | # assert_equal '40 KB', 41010.to_s(:human_size, :precision => 1) 367 | # assert_equal '40 KB', 41100.to_s(:human_size, :precision => 2) 368 | # assert_equal '1.0 KB', kilobytes(1.0123).to_s(:human_size, :precision => 2, :strip_insignificant_zeros => false) 369 | # assert_equal '1.012 KB', kilobytes(1.0123).to_s(:human_size, :precision => 3, :significant => false) 370 | # assert_equal '1 KB', kilobytes(1.0123).to_s(:human_size, :precision => 0, :significant => true) #ignores significant it precision is 0 371 | # end 372 | # 373 | # def test_to_s__human_size_with_custom_delimiter_and_separator 374 | # assert_equal '1,01 KB', kilobytes(1.0123).to_s(:human_size, :precision => 3, :separator => ',') 375 | # assert_equal '1,01 KB', kilobytes(1.0100).to_s(:human_size, :precision => 4, :separator => ',') 376 | # assert_equal '1.000,1 TB', terabytes(1000.1).to_s(:human_size, :precision => 5, :delimiter => '.', :separator => ',') 377 | # end 378 | # 379 | # def test_number_to_human 380 | # assert_equal '-123', -123.to_s(:human) 381 | # assert_equal '-0.5', -0.5.to_s(:human) 382 | # assert_equal '0', 0.to_s(:human) 383 | # assert_equal '0.5', 0.5.to_s(:human) 384 | # assert_equal '123', 123.to_s(:human) 385 | # assert_equal '1.23 Thousand', 1234.to_s(:human) 386 | # assert_equal '12.3 Thousand', 12345.to_s(:human) 387 | # assert_equal '1.23 Million', 1234567.to_s(:human) 388 | # assert_equal '1.23 Billion', 1234567890.to_s(:human) 389 | # assert_equal '1.23 Trillion', 1234567890123.to_s(:human) 390 | # assert_equal '1.23 Quadrillion', 1234567890123456.to_s(:human) 391 | # assert_equal '1230 Quadrillion', 1234567890123456789.to_s(:human) 392 | # assert_equal '490 Thousand', 489939.to_s(:human, :precision => 2) 393 | # assert_equal '489.9 Thousand', 489939.to_s(:human, :precision => 4) 394 | # assert_equal '489 Thousand', 489000.to_s(:human, :precision => 4) 395 | # assert_equal '489.0 Thousand', 489000.to_s(:human, :precision => 4, :strip_insignificant_zeros => false) 396 | # assert_equal '1.2346 Million', 1234567.to_s(:human, :precision => 4, :significant => false) 397 | # assert_equal '1,2 Million', 1234567.to_s(:human, :precision => 1, :significant => false, :separator => ',') 398 | # assert_equal '1 Million', 1234567.to_s(:human, :precision => 0, :significant => true, :separator => ',') #significant forced to false 399 | # end 400 | # 401 | # def test_number_to_human_with_custom_units 402 | # #Only integers 403 | # volume = {:unit => "ml", :thousand => "lt", :million => "m3"} 404 | # assert_equal '123 lt', 123456.to_s(:human, :units => volume) 405 | # assert_equal '12 ml', 12.to_s(:human, :units => volume) 406 | # assert_equal '1.23 m3', 1234567.to_s(:human, :units => volume) 407 | # 408 | # #Including fractionals 409 | # distance = {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"} 410 | # assert_equal '1.23 mm', 0.00123.to_s(:human, :units => distance) 411 | # assert_equal '1.23 cm', 0.0123.to_s(:human, :units => distance) 412 | # assert_equal '1.23 dm', 0.123.to_s(:human, :units => distance) 413 | # assert_equal '1.23 m', 1.23.to_s(:human, :units => distance) 414 | # assert_equal '1.23 dam', 12.3.to_s(:human, :units => distance) 415 | # assert_equal '1.23 hm', 123.to_s(:human, :units => distance) 416 | # assert_equal '1.23 km', 1230.to_s(:human, :units => distance) 417 | # assert_equal '1.23 km', 1230.to_s(:human, :units => distance) 418 | # assert_equal '1.23 km', 1230.to_s(:human, :units => distance) 419 | # assert_equal '12.3 km', 12300.to_s(:human, :units => distance) 420 | # 421 | # #The quantifiers don't need to be a continuous sequence 422 | # gangster = {:hundred => "hundred bucks", :million => "thousand quids"} 423 | # assert_equal '1 hundred bucks', 100.to_s(:human, :units => gangster) 424 | # assert_equal '25 hundred bucks', 2500.to_s(:human, :units => gangster) 425 | # assert_equal '25 thousand quids', 25000000.to_s(:human, :units => gangster) 426 | # assert_equal '12300 thousand quids', 12345000000.to_s(:human, :units => gangster) 427 | # 428 | # #Spaces are stripped from the resulting string 429 | # assert_equal '4', 4.to_s(:human, :units => {:unit => "", :ten => 'tens '}) 430 | # assert_equal '4.5 tens', 45.to_s(:human, :units => {:unit => "", :ten => ' tens '}) 431 | # end 432 | # 433 | # def test_number_to_human_with_custom_format 434 | # assert_equal '123 times Thousand', 123456.to_s(:human, :format => "%n times %u") 435 | # volume = {:unit => "ml", :thousand => "lt", :million => "m3"} 436 | # assert_equal '123.lt', 123456.to_s(:human, :units => volume, :format => "%n.%u") 437 | # end 438 | # 439 | # def test_to_s__injected_on_proper_types 440 | # assert_equal Fixnum, 1230.class 441 | # assert_equal '1.23 Thousand', 1230.to_s(:human) 442 | # 443 | # assert_equal Float, Float(1230).class 444 | # assert_equal '1.23 Thousand', Float(1230).to_s(:human) 445 | # 446 | # assert_equal Bignum, (100**10).class 447 | # assert_equal '100000 Quadrillion', (100**10).to_s(:human) 448 | # 449 | # assert_equal BigDecimal, BigDecimal("1000010").class 450 | # assert_equal '1 Million', BigDecimal("1000010").to_s(:human) 451 | # end 452 | # end 453 | # 454 | # class NumericExtBehaviorTest < ActiveSupport::TestCase 455 | # def setup 456 | # @inf = BigDecimal.new('Infinity') 457 | # end 458 | # 459 | # def test_compare_infinity_with_date 460 | # assert_equal(-1, -Float::INFINITY <=> Date.today) 461 | # assert_equal(1, Float::INFINITY <=> Date.today) 462 | # assert_equal(-1, -@inf <=> Date.today) 463 | # assert_equal(1, @inf <=> Date.today) 464 | # end 465 | # 466 | # def test_compare_infinty_with_infinty 467 | # assert_equal(-1, -Float::INFINITY <=> Float::INFINITY) 468 | # assert_equal(1, Float::INFINITY <=> -Float::INFINITY) 469 | # assert_equal(0, Float::INFINITY <=> Float::INFINITY) 470 | # assert_equal(0, -Float::INFINITY <=> -Float::INFINITY) 471 | # 472 | # assert_equal(-1, -Float::INFINITY <=> BigDecimal::INFINITY) 473 | # assert_equal(1, Float::INFINITY <=> -BigDecimal::INFINITY) 474 | # assert_equal(0, Float::INFINITY <=> BigDecimal::INFINITY) 475 | # assert_equal(0, -Float::INFINITY <=> -BigDecimal::INFINITY) 476 | # 477 | # assert_equal(-1, -BigDecimal::INFINITY <=> Float::INFINITY) 478 | # assert_equal(1, BigDecimal::INFINITY <=> -Float::INFINITY) 479 | # assert_equal(0, BigDecimal::INFINITY <=> Float::INFINITY) 480 | # assert_equal(0, -BigDecimal::INFINITY <=> -Float::INFINITY) 481 | # end 482 | # 483 | # def test_compare_infinity_with_time 484 | # assert_equal(-1, -Float::INFINITY <=> Time.now) 485 | # assert_equal(1, Float::INFINITY <=> Time.now) 486 | # assert_equal(-1, -@inf <=> Time.now) 487 | # assert_equal(1, @inf <=> Time.now) 488 | # end 489 | # 490 | # def test_compare_infinity_with_datetime 491 | # assert_equal(-1, -Float::INFINITY <=> DateTime.now) 492 | # assert_equal(1, Float::INFINITY <=> DateTime.now) 493 | # assert_equal(-1, -@inf <=> DateTime.now) 494 | # assert_equal(1, @inf <=> DateTime.now) 495 | # end 496 | # 497 | # def test_compare_infinity_with_twz 498 | # time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] 499 | # twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone) 500 | # 501 | # assert_equal(-1, -Float::INFINITY <=> twz) 502 | # assert_equal(1, Float::INFINITY <=> twz) 503 | # assert_equal(-1, -@inf <=> twz) 504 | # assert_equal(1, @inf <=> twz) 505 | # end 506 | # end 507 | -------------------------------------------------------------------------------- /test/core_ext/string_ext_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # require "date" 4 | require "abstract_unit" 5 | # require "timeout" 6 | require "inflector_test_cases" 7 | require "constantize_test_cases" 8 | 9 | require "active_support/inflector" 10 | require "active_support/core_ext/string" 11 | # require "active_support/time" 12 | # require "active_support/core_ext/string/strip" 13 | # require "active_support/core_ext/string/output_safety" 14 | # require "active_support/core_ext/string/indent" 15 | # require "time_zone_test_helpers" 16 | # require "yaml" 17 | 18 | class StringInflectionsTest < ActiveSupport::TestCase 19 | include InflectorTestCases 20 | include ConstantizeTestCases 21 | # include TimeZoneTestHelpers 22 | 23 | # def test_strip_heredoc_on_an_empty_string 24 | # assert_equal "", "".strip_heredoc 25 | # end 26 | 27 | # def test_strip_heredoc_on_a_string_with_no_lines 28 | # assert_equal "x", "x".strip_heredoc 29 | # assert_equal "x", " x".strip_heredoc 30 | # end 31 | 32 | # def test_strip_heredoc_on_a_heredoc_with_no_margin 33 | # assert_equal "foo\nbar", "foo\nbar".strip_heredoc 34 | # assert_equal "foo\n bar", "foo\n bar".strip_heredoc 35 | # end 36 | 37 | # def test_strip_heredoc_on_a_regular_indented_heredoc 38 | # assert_equal "foo\n bar\nbaz\n", <<-EOS.strip_heredoc 39 | # foo 40 | # bar 41 | # baz 42 | # EOS 43 | # end 44 | 45 | # def test_strip_heredoc_on_a_regular_indented_heredoc_with_blank_lines 46 | # assert_equal "foo\n bar\n\nbaz\n", <<-EOS.strip_heredoc 47 | # foo 48 | # bar 49 | 50 | # baz 51 | # EOS 52 | # end 53 | 54 | def test_pluralize 55 | SingularToPlural.each do |singular, plural| 56 | assert_equal(plural, singular.pluralize) 57 | end 58 | 59 | assert_equal("plurals", "plurals".pluralize) 60 | 61 | assert_equal("blargles", "blargle".pluralize(0)) 62 | assert_equal("blargle", "blargle".pluralize(1)) 63 | assert_equal("blargles", "blargle".pluralize(2)) 64 | end 65 | 66 | # BROKEN: this spec can't pass for Opal 67 | # because String#equal? returns true for different objects representing the same string 68 | # i.e. string.equal?(string.dup) => true 69 | # 70 | # test "pluralize with count = 1 still returns new string" do 71 | # name = "Kuldeep" 72 | # assert_not_same name.pluralize(1), name 73 | # end 74 | 75 | def test_singularize 76 | SingularToPlural.each do |singular, plural| 77 | assert_equal(singular, plural.singularize) 78 | end 79 | end 80 | 81 | def test_titleize 82 | MixtureToTitleCase.each do |before, titleized| 83 | assert_equal(titleized, before.titleize) 84 | end 85 | end 86 | 87 | def test_titleize_with_keep_id_suffix 88 | MixtureToTitleCaseWithKeepIdSuffix.each do |before, titleized| 89 | assert_equal(titleized, before.titleize(keep_id_suffix: true)) 90 | end 91 | end 92 | 93 | def test_upcase_first 94 | assert_equal "What a Lovely Day", "what a Lovely Day".upcase_first 95 | end 96 | 97 | def test_upcase_first_with_one_char 98 | assert_equal "W", "w".upcase_first 99 | end 100 | 101 | def test_upcase_first_with_empty_string 102 | assert_equal "", "".upcase_first 103 | end 104 | 105 | def test_camelize 106 | CamelToUnderscore.each do |camel, underscore| 107 | assert_equal(camel, underscore.camelize) 108 | end 109 | end 110 | 111 | def test_camelize_lower 112 | assert_equal("capital", "Capital".camelize(:lower)) 113 | end 114 | 115 | def test_camelize_invalid_option 116 | e = assert_raise ArgumentError do 117 | "Capital".camelize(nil) 118 | end 119 | assert_equal("Invalid option, use either :upper or :lower.", e.message) 120 | end 121 | 122 | def test_dasherize 123 | UnderscoresToDashes.each do |underscored, dasherized| 124 | assert_equal(dasherized, underscored.dasherize) 125 | end 126 | end 127 | 128 | def test_underscore 129 | CamelToUnderscore.each do |camel, underscore| 130 | assert_equal(underscore, camel.underscore) 131 | end 132 | 133 | assert_equal "html_tidy", "HTMLTidy".underscore 134 | assert_equal "html_tidy_generator", "HTMLTidyGenerator".underscore 135 | end 136 | 137 | def test_underscore_to_lower_camel 138 | UnderscoreToLowerCamel.each do |underscored, lower_camel| 139 | assert_equal(lower_camel, underscored.camelize(:lower)) 140 | end 141 | end 142 | 143 | def test_demodulize 144 | assert_equal "Account", "MyApplication::Billing::Account".demodulize 145 | end 146 | 147 | def test_deconstantize 148 | assert_equal "MyApplication::Billing", "MyApplication::Billing::Account".deconstantize 149 | end 150 | 151 | def test_foreign_key 152 | ClassNameToForeignKeyWithUnderscore.each do |klass, foreign_key| 153 | assert_equal(foreign_key, klass.foreign_key) 154 | end 155 | 156 | ClassNameToForeignKeyWithoutUnderscore.each do |klass, foreign_key| 157 | assert_equal(foreign_key, klass.foreign_key(false)) 158 | end 159 | end 160 | 161 | def test_tableize 162 | ClassNameToTableName.each do |class_name, table_name| 163 | assert_equal(table_name, class_name.tableize) 164 | end 165 | end 166 | 167 | def test_classify 168 | ClassNameToTableName.each do |class_name, table_name| 169 | assert_equal(class_name, table_name.classify) 170 | end 171 | end 172 | 173 | # def test_string_parameterized_normal 174 | # StringToParameterized.each do |normal, slugged| 175 | # assert_equal(slugged, normal.parameterize) 176 | # end 177 | # end 178 | 179 | # def test_string_parameterized_normal_preserve_case 180 | # StringToParameterizedPreserveCase.each do |normal, slugged| 181 | # assert_equal(slugged, normal.parameterize(preserve_case: true)) 182 | # end 183 | # end 184 | 185 | # def test_string_parameterized_no_separator 186 | # StringToParameterizeWithNoSeparator.each do |normal, slugged| 187 | # assert_equal(slugged, normal.parameterize(separator: "")) 188 | # end 189 | # end 190 | 191 | # def test_string_parameterized_no_separator_preserve_case 192 | # StringToParameterizePreserveCaseWithNoSeparator.each do |normal, slugged| 193 | # assert_equal(slugged, normal.parameterize(separator: "", preserve_case: true)) 194 | # end 195 | # end 196 | 197 | # def test_string_parameterized_underscore 198 | # StringToParameterizeWithUnderscore.each do |normal, slugged| 199 | # assert_equal(slugged, normal.parameterize(separator: "_")) 200 | # end 201 | # end 202 | 203 | # def test_string_parameterized_underscore_preserve_case 204 | # StringToParameterizePreserveCaseWithUnderscore.each do |normal, slugged| 205 | # assert_equal(slugged, normal.parameterize(separator: "_", preserve_case: true)) 206 | # end 207 | # end 208 | 209 | def test_humanize 210 | UnderscoreToHuman.each do |underscore, human| 211 | assert_equal(human, underscore.humanize) 212 | end 213 | end 214 | 215 | def test_humanize_without_capitalize 216 | UnderscoreToHumanWithoutCapitalize.each do |underscore, human| 217 | assert_equal(human, underscore.humanize(capitalize: false)) 218 | end 219 | end 220 | 221 | def test_humanize_with_keep_id_suffix 222 | UnderscoreToHumanWithKeepIdSuffix.each do |underscore, human| 223 | assert_equal(human, underscore.humanize(keep_id_suffix: true)) 224 | end 225 | end 226 | 227 | # def test_humanize_with_html_escape 228 | # assert_equal "Hello", ERB::Util.html_escape("hello").humanize 229 | # end 230 | 231 | # def test_ord 232 | # assert_equal 97, "a".ord 233 | # assert_equal 97, "abc".ord 234 | # end 235 | 236 | # def test_starts_ends_with_alias 237 | # s = "hello" 238 | # assert s.starts_with?("h") 239 | # assert s.starts_with?("hel") 240 | # assert !s.starts_with?("el") 241 | 242 | # assert s.ends_with?("o") 243 | # assert s.ends_with?("lo") 244 | # assert !s.ends_with?("el") 245 | # end 246 | 247 | # def test_string_squish 248 | # original = %{\u205f\u3000 A string surrounded by various unicode spaces, 249 | # with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u00a0\u2007}.dup 250 | 251 | # expected = "A string surrounded by various unicode spaces, " \ 252 | # "with tabs( ), newlines( ), unicode nextlines( ) and many spaces( )." 253 | 254 | # # Make sure squish returns what we expect: 255 | # assert_equal expected, original.squish 256 | # # But doesn't modify the original string: 257 | # assert_not_equal expected, original 258 | 259 | # # Make sure squish! returns what we expect: 260 | # assert_equal expected, original.squish! 261 | # # And changes the original string: 262 | # assert_equal expected, original 263 | # end 264 | 265 | # def test_string_inquiry 266 | # assert_predicate "production".inquiry, :production? 267 | # assert_not_predicate "production".inquiry, :development? 268 | # end 269 | 270 | def test_truncate 271 | assert_equal "Hello World!", "Hello World!".truncate(12) 272 | assert_equal "Hello Wor...", "Hello World!!".truncate(12) 273 | end 274 | 275 | def test_truncate_with_omission_and_separator 276 | assert_equal "Hello[...]", "Hello World!".truncate(10, omission: "[...]") 277 | assert_equal "Hello[...]", "Hello Big World!".truncate(13, omission: "[...]", separator: " ") 278 | assert_equal "Hello Big[...]", "Hello Big World!".truncate(14, omission: "[...]", separator: " ") 279 | assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, omission: "[...]", separator: " ") 280 | end 281 | 282 | def test_truncate_with_omission_and_regexp_separator 283 | assert_equal "Hello[...]", "Hello Big World!".truncate(13, omission: "[...]", separator: /\s/) 284 | assert_equal "Hello Big[...]", "Hello Big World!".truncate(14, omission: "[...]", separator: /\s/) 285 | assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, omission: "[...]", separator: /\s/) 286 | end 287 | 288 | # def test_truncate_bytes 289 | # assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16) 290 | # assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16, omission: nil) 291 | # assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16, omission: " ") 292 | # assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16, omission: "🖖") 293 | # 294 | # assert_equal "👍👍👍…", "👍👍👍👍".truncate_bytes(15) 295 | # assert_equal "👍👍👍", "👍👍👍👍".truncate_bytes(15, omission: nil) 296 | # assert_equal "👍👍👍 ", "👍👍👍👍".truncate_bytes(15, omission: " ") 297 | # assert_equal "👍👍🖖", "👍👍👍👍".truncate_bytes(15, omission: "🖖") 298 | # 299 | # assert_equal "…", "👍👍👍👍".truncate_bytes(5) 300 | # assert_equal "👍", "👍👍👍👍".truncate_bytes(5, omission: nil) 301 | # assert_equal "👍 ", "👍👍👍👍".truncate_bytes(5, omission: " ") 302 | # assert_equal "🖖", "👍👍👍👍".truncate_bytes(5, omission: "🖖") 303 | # 304 | # assert_equal "…", "👍👍👍👍".truncate_bytes(4) 305 | # assert_equal "👍", "👍👍👍👍".truncate_bytes(4, omission: nil) 306 | # assert_equal " ", "👍👍👍👍".truncate_bytes(4, omission: " ") 307 | # assert_equal "🖖", "👍👍👍👍".truncate_bytes(4, omission: "🖖") 308 | # 309 | # assert_raise ArgumentError do 310 | # "👍👍👍👍".truncate_bytes(3, omission: "🖖") 311 | # end 312 | # end 313 | # 314 | # def test_truncate_bytes_preserves_codepoints 315 | # assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16) 316 | # assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16, omission: nil) 317 | # assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16, omission: " ") 318 | # assert_equal "👍👍👍👍", "👍👍👍👍".truncate_bytes(16, omission: "🖖") 319 | # 320 | # assert_equal "👍👍👍…", "👍👍👍👍".truncate_bytes(15) 321 | # assert_equal "👍👍👍", "👍👍👍👍".truncate_bytes(15, omission: nil) 322 | # assert_equal "👍👍👍 ", "👍👍👍👍".truncate_bytes(15, omission: " ") 323 | # assert_equal "👍👍🖖", "👍👍👍👍".truncate_bytes(15, omission: "🖖") 324 | # 325 | # assert_equal "…", "👍👍👍👍".truncate_bytes(5) 326 | # assert_equal "👍", "👍👍👍👍".truncate_bytes(5, omission: nil) 327 | # assert_equal "👍 ", "👍👍👍👍".truncate_bytes(5, omission: " ") 328 | # assert_equal "🖖", "👍👍👍👍".truncate_bytes(5, omission: "🖖") 329 | # 330 | # assert_equal "…", "👍👍👍👍".truncate_bytes(4) 331 | # assert_equal "👍", "👍👍👍👍".truncate_bytes(4, omission: nil) 332 | # assert_equal " ", "👍👍👍👍".truncate_bytes(4, omission: " ") 333 | # assert_equal "🖖", "👍👍👍👍".truncate_bytes(4, omission: "🖖") 334 | # 335 | # assert_raise ArgumentError do 336 | # "👍👍👍👍".truncate_bytes(3, omission: "🖖") 337 | # end 338 | # end 339 | # 340 | # def test_truncates_bytes_preserves_grapheme_clusters 341 | # assert_equal "a ", "a ❤️ b".truncate_bytes(2, omission: nil) 342 | # assert_equal "a ", "a ❤️ b".truncate_bytes(3, omission: nil) 343 | # assert_equal "a ", "a ❤️ b".truncate_bytes(7, omission: nil) 344 | # assert_equal "a ❤️", "a ❤️ b".truncate_bytes(8, omission: nil) 345 | # 346 | # assert_equal "a ", "a 👩‍❤️‍👩".truncate_bytes(13, omission: nil) 347 | # assert_equal "", "👩‍❤️‍👩".truncate_bytes(13, omission: nil) 348 | # end 349 | # 350 | # def test_truncate_words 351 | # assert_equal "Hello Big World!", "Hello Big World!".truncate_words(3) 352 | # assert_equal "Hello Big...", "Hello Big World!".truncate_words(2) 353 | # end 354 | 355 | # def test_truncate_words_with_omission 356 | # assert_equal "Hello Big World!", "Hello Big World!".truncate_words(3, omission: "[...]") 357 | # assert_equal "Hello Big[...]", "Hello Big World!".truncate_words(2, omission: "[...]") 358 | # end 359 | 360 | # def test_truncate_words_with_separator 361 | # assert_equal "Hello
Big
World!...", "Hello
Big
World!
".truncate_words(3, separator: "
") 362 | # assert_equal "Hello
Big
World!", "Hello
Big
World!".truncate_words(3, separator: "
") 363 | # assert_equal "Hello\n
Big...", "Hello\n
Big
Wide
World!".truncate_words(2, separator: "
") 364 | # end 365 | 366 | # def test_truncate_words_with_separator_and_omission 367 | # assert_equal "Hello
Big
World![...]", "Hello
Big
World!
".truncate_words(3, omission: "[...]", separator: "
") 368 | # assert_equal "Hello
Big
World!", "Hello
Big
World!".truncate_words(3, omission: "[...]", separator: "
") 369 | # end 370 | 371 | # def test_truncate_words_with_complex_string 372 | # Timeout.timeout(10) do 373 | # complex_string = "aa aa aaa aa aaa aaa aaa aa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaa aaaa aaaaa aaaaa aaaaaa aa aa aa aaa aa aaa aa aa aa aa a aaa aaa \n a aaa <= string length still returns a new string" do 467 | # string = "hello" 468 | # different_string = string.first(5) 469 | # assert_not_same different_string, string 470 | # end 471 | 472 | # test "#last returns the last character" do 473 | # assert_equal "o", "hello".last 474 | # assert_equal "x", "x".last 475 | # end 476 | 477 | # test "#last with Integer, returns a substring from the end to position" do 478 | # assert_equal "llo", "hello".last(3) 479 | # assert_equal "hello", "hello".last(10) 480 | # assert_equal "", "hello".last(0) 481 | # assert_equal "x", "x".last(4) 482 | # end 483 | 484 | # test "#last with Integer >= string length still returns a new string" do 485 | # string = "hello" 486 | # different_string = string.last(5) 487 | # assert_not_same different_string, string 488 | # end 489 | 490 | # test "access returns a real string" do 491 | # hash = {} 492 | # hash["h"] = true 493 | # hash["hello123".at(0)] = true 494 | # assert_equal %w(h), hash.keys 495 | 496 | # hash = {} 497 | # hash["llo"] = true 498 | # hash["hello".from(2)] = true 499 | # assert_equal %w(llo), hash.keys 500 | 501 | # hash = {} 502 | # hash["hel"] = true 503 | # hash["hello".to(2)] = true 504 | # assert_equal %w(hel), hash.keys 505 | 506 | # hash = {} 507 | # hash["hello"] = true 508 | # hash["123hello".last(5)] = true 509 | # assert_equal %w(hello), hash.keys 510 | 511 | # hash = {} 512 | # hash["hello"] = true 513 | # hash["hello123".first(5)] = true 514 | # assert_equal %w(hello), hash.keys 515 | # end 516 | # end 517 | 518 | # class StringConversionsTest < ActiveSupport::TestCase 519 | # include TimeZoneTestHelpers 520 | 521 | # def test_string_to_time 522 | # with_env_tz "Europe/Moscow" do 523 | # assert_equal Time.utc(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time(:utc) 524 | # assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time 525 | # assert_equal Time.utc(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time(:utc) 526 | # assert_equal Time.local(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time 527 | # assert_equal Time.utc(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time(:utc) 528 | # assert_equal Time.local(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time 529 | # assert_equal Time.local(2011, 2, 27, 17, 50), "2011-02-27 13:50 -0100".to_time 530 | # assert_equal Time.utc(2011, 2, 27, 23, 50), "2011-02-27 22:50 -0100".to_time(:utc) 531 | # assert_equal Time.local(2005, 2, 27, 22, 50), "2005-02-27 14:50 -0500".to_time 532 | # assert_nil "010".to_time 533 | # assert_nil "".to_time 534 | # end 535 | # end 536 | 537 | # def test_string_to_time_utc_offset 538 | # with_env_tz "US/Eastern" do 539 | # if ActiveSupport.to_time_preserves_timezone 540 | # assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset 541 | # assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset) 542 | # assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset 543 | # assert_equal(-3600, "2005-02-27 22:50 -0100".to_time.utc_offset) 544 | # else 545 | # assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset 546 | # assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset) 547 | # assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset 548 | # assert_equal(-18000, "2005-02-27 22:50 -0100".to_time.utc_offset) 549 | # end 550 | # end 551 | # end 552 | 553 | # def test_partial_string_to_time 554 | # with_env_tz "Europe/Moscow" do # use timezone which does not observe DST. 555 | # now = Time.now 556 | # assert_equal Time.local(now.year, now.month, now.day, 23, 50), "23:50".to_time 557 | # assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "23:50".to_time(:utc) 558 | # assert_equal Time.local(now.year, now.month, now.day, 17, 50), "13:50 -0100".to_time 559 | # assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "22:50 -0100".to_time(:utc) 560 | # end 561 | # end 562 | 563 | # def test_standard_time_string_to_time_when_current_time_is_standard_time 564 | # with_env_tz "US/Eastern" do 565 | # Time.stub(:now, Time.local(2012, 1, 1)) do 566 | # assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time 567 | # assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc) 568 | # assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time 569 | # assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc) 570 | # assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time 571 | # assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc) 572 | # assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time 573 | # assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc) 574 | # assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time 575 | # assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc) 576 | # assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time 577 | # assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc) 578 | # end 579 | # end 580 | # end 581 | 582 | # def test_standard_time_string_to_time_when_current_time_is_daylight_savings 583 | # with_env_tz "US/Eastern" do 584 | # Time.stub(:now, Time.local(2012, 7, 1)) do 585 | # assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time 586 | # assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc) 587 | # assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time 588 | # assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc) 589 | # assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time 590 | # assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc) 591 | # assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time 592 | # assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc) 593 | # assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time 594 | # assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc) 595 | # assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time 596 | # assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc) 597 | # end 598 | # end 599 | # end 600 | 601 | # def test_daylight_savings_string_to_time_when_current_time_is_standard_time 602 | # with_env_tz "US/Eastern" do 603 | # Time.stub(:now, Time.local(2012, 1, 1)) do 604 | # assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time 605 | # assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc) 606 | # assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time 607 | # assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc) 608 | # assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time 609 | # assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc) 610 | # assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time 611 | # assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc) 612 | # assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time 613 | # assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc) 614 | # assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time 615 | # assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc) 616 | # end 617 | # end 618 | # end 619 | 620 | # def test_daylight_savings_string_to_time_when_current_time_is_daylight_savings 621 | # with_env_tz "US/Eastern" do 622 | # Time.stub(:now, Time.local(2012, 7, 1)) do 623 | # assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time 624 | # assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc) 625 | # assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time 626 | # assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc) 627 | # assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time 628 | # assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc) 629 | # assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time 630 | # assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc) 631 | # assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time 632 | # assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc) 633 | # assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time 634 | # assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc) 635 | # end 636 | # end 637 | # end 638 | 639 | # def test_partial_string_to_time_when_current_time_is_standard_time 640 | # with_env_tz "US/Eastern" do 641 | # Time.stub(:now, Time.local(2012, 1, 1)) do 642 | # assert_equal Time.local(2012, 1, 1, 10, 0), "10:00".to_time 643 | # assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00".to_time(:utc) 644 | # assert_equal Time.local(2012, 1, 1, 6, 0), "10:00 -0100".to_time 645 | # assert_equal Time.utc(2012, 1, 1, 11, 0), "10:00 -0100".to_time(:utc) 646 | # assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 -0500".to_time 647 | # assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 -0500".to_time(:utc) 648 | # assert_equal Time.local(2012, 1, 1, 5, 0), "10:00 UTC".to_time 649 | # assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00 UTC".to_time(:utc) 650 | # assert_equal Time.local(2012, 1, 1, 13, 0), "10:00 PST".to_time 651 | # assert_equal Time.utc(2012, 1, 1, 18, 0), "10:00 PST".to_time(:utc) 652 | # assert_equal Time.local(2012, 1, 1, 12, 0), "10:00 PDT".to_time 653 | # assert_equal Time.utc(2012, 1, 1, 17, 0), "10:00 PDT".to_time(:utc) 654 | # assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 EST".to_time 655 | # assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 EST".to_time(:utc) 656 | # assert_equal Time.local(2012, 1, 1, 9, 0), "10:00 EDT".to_time 657 | # assert_equal Time.utc(2012, 1, 1, 14, 0), "10:00 EDT".to_time(:utc) 658 | # end 659 | # end 660 | # end 661 | 662 | # def test_partial_string_to_time_when_current_time_is_daylight_savings 663 | # with_env_tz "US/Eastern" do 664 | # Time.stub(:now, Time.local(2012, 7, 1)) do 665 | # assert_equal Time.local(2012, 7, 1, 10, 0), "10:00".to_time 666 | # assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00".to_time(:utc) 667 | # assert_equal Time.local(2012, 7, 1, 7, 0), "10:00 -0100".to_time 668 | # assert_equal Time.utc(2012, 7, 1, 11, 0), "10:00 -0100".to_time(:utc) 669 | # assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 -0500".to_time 670 | # assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 -0500".to_time(:utc) 671 | # assert_equal Time.local(2012, 7, 1, 6, 0), "10:00 UTC".to_time 672 | # assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00 UTC".to_time(:utc) 673 | # assert_equal Time.local(2012, 7, 1, 14, 0), "10:00 PST".to_time 674 | # assert_equal Time.utc(2012, 7, 1, 18, 0), "10:00 PST".to_time(:utc) 675 | # assert_equal Time.local(2012, 7, 1, 13, 0), "10:00 PDT".to_time 676 | # assert_equal Time.utc(2012, 7, 1, 17, 0), "10:00 PDT".to_time(:utc) 677 | # assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 EST".to_time 678 | # assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 EST".to_time(:utc) 679 | # assert_equal Time.local(2012, 7, 1, 10, 0), "10:00 EDT".to_time 680 | # assert_equal Time.utc(2012, 7, 1, 14, 0), "10:00 EDT".to_time(:utc) 681 | # end 682 | # end 683 | # end 684 | 685 | # def test_string_to_datetime 686 | # assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_datetime 687 | # assert_equal 0, "2039-02-27 23:50".to_datetime.offset # use UTC offset 688 | # assert_equal ::Date::ITALY, "2039-02-27 23:50".to_datetime.start # use Ruby's default start value 689 | # assert_equal DateTime.civil(2039, 2, 27, 23, 50, 19 + Rational(275038, 1000000), "-04:00"), "2039-02-27T23:50:19.275038-04:00".to_datetime 690 | # assert_nil "".to_datetime 691 | # end 692 | 693 | # def test_partial_string_to_datetime 694 | # now = DateTime.now 695 | # assert_equal DateTime.civil(now.year, now.month, now.day, 23, 50), "23:50".to_datetime 696 | # assert_equal DateTime.civil(now.year, now.month, now.day, 23, 50, 0, "-04:00"), "23:50 -0400".to_datetime 697 | # end 698 | 699 | # def test_string_to_date 700 | # assert_equal Date.new(2005, 2, 27), "2005-02-27".to_date 701 | # assert_nil "".to_date 702 | # assert_equal Date.new(Date.today.year, 2, 3), "Feb 3rd".to_date 703 | # end 704 | # end 705 | 706 | # class StringBehaviourTest < ActiveSupport::TestCase 707 | # def test_acts_like_string 708 | # assert_predicate "Bambi", :acts_like_string? 709 | # end 710 | # end 711 | 712 | # class CoreExtStringMultibyteTest < ActiveSupport::TestCase 713 | # UTF8_STRING = "こにちわ" 714 | # ASCII_STRING = "ohayo".encode("US-ASCII") 715 | # EUC_JP_STRING = "さよなら".encode("EUC-JP") 716 | # INVALID_UTF8_STRING = "\270\236\010\210\245" 717 | 718 | # def test_core_ext_adds_mb_chars 719 | # assert_respond_to UTF8_STRING, :mb_chars 720 | # end 721 | 722 | # def test_string_should_recognize_utf8_strings 723 | # assert_predicate UTF8_STRING, :is_utf8? 724 | # assert_predicate ASCII_STRING, :is_utf8? 725 | # assert_not_predicate EUC_JP_STRING, :is_utf8? 726 | # assert_not_predicate INVALID_UTF8_STRING, :is_utf8? 727 | # end 728 | 729 | # def test_mb_chars_returns_instance_of_proxy_class 730 | # assert_kind_of ActiveSupport::Multibyte.proxy_class, UTF8_STRING.mb_chars 731 | # end 732 | # end 733 | 734 | # class OutputSafetyTest < ActiveSupport::TestCase 735 | # def setup 736 | # @string = "hello".dup 737 | # @object = Class.new(Object) do 738 | # def to_s 739 | # "other" 740 | # end 741 | # end.new 742 | # end 743 | 744 | # test "A string is unsafe by default" do 745 | # assert_not_predicate @string, :html_safe? 746 | # end 747 | 748 | # test "A string can be marked safe" do 749 | # string = @string.html_safe 750 | # assert_predicate string, :html_safe? 751 | # end 752 | 753 | # test "Marking a string safe returns the string" do 754 | # assert_equal @string, @string.html_safe 755 | # end 756 | 757 | # test "An integer is safe by default" do 758 | # assert_predicate 5, :html_safe? 759 | # end 760 | 761 | # test "a float is safe by default" do 762 | # assert_predicate 5.7, :html_safe? 763 | # end 764 | 765 | # test "An object is unsafe by default" do 766 | # assert_not_predicate @object, :html_safe? 767 | # end 768 | 769 | # test "Adding an object to a safe string returns a safe string" do 770 | # string = @string.html_safe 771 | # string << @object 772 | 773 | # assert_equal "helloother", string 774 | # assert_predicate string, :html_safe? 775 | # end 776 | 777 | # test "Adding a safe string to another safe string returns a safe string" do 778 | # @other_string = "other".html_safe 779 | # string = @string.html_safe 780 | # @combination = @other_string + string 781 | 782 | # assert_equal "otherhello", @combination 783 | # assert_predicate @combination, :html_safe? 784 | # end 785 | 786 | # test "Adding an unsafe string to a safe string escapes it and returns a safe string" do 787 | # @other_string = "other".html_safe 788 | # @combination = @other_string + "" 789 | # @other_combination = @string + "" 790 | 791 | # assert_equal "other<foo>", @combination 792 | # assert_equal "hello", @other_combination 793 | 794 | # assert_predicate @combination, :html_safe? 795 | # assert_not_predicate @other_combination, :html_safe? 796 | # end 797 | 798 | # test "Prepending safe onto unsafe yields unsafe" do 799 | # @string.prepend "other".html_safe 800 | # assert_not_predicate @string, :html_safe? 801 | # assert_equal "otherhello", @string 802 | # end 803 | 804 | # test "Prepending unsafe onto safe yields escaped safe" do 805 | # other = "other".html_safe 806 | # other.prepend "" 807 | # assert_predicate other, :html_safe? 808 | # assert_equal "<foo>other", other 809 | # end 810 | 811 | # test "Concatting safe onto unsafe yields unsafe" do 812 | # @other_string = "other".dup 813 | 814 | # string = @string.html_safe 815 | # @other_string.concat(string) 816 | # assert_not_predicate @other_string, :html_safe? 817 | # end 818 | 819 | # test "Concatting unsafe onto safe yields escaped safe" do 820 | # @other_string = "other".html_safe 821 | # string = @other_string.concat("") 822 | # assert_equal "other<foo>", string 823 | # assert_predicate string, :html_safe? 824 | # end 825 | 826 | # test "Concatting safe onto safe yields safe" do 827 | # @other_string = "other".html_safe 828 | # string = @string.html_safe 829 | 830 | # @other_string.concat(string) 831 | # assert_predicate @other_string, :html_safe? 832 | # end 833 | 834 | # test "Concatting safe onto unsafe with << yields unsafe" do 835 | # @other_string = "other".dup 836 | # string = @string.html_safe 837 | 838 | # @other_string << string 839 | # assert_not_predicate @other_string, :html_safe? 840 | # end 841 | 842 | # test "Concatting unsafe onto safe with << yields escaped safe" do 843 | # @other_string = "other".html_safe 844 | # string = @other_string << "" 845 | # assert_equal "other<foo>", string 846 | # assert_predicate string, :html_safe? 847 | # end 848 | 849 | # test "Concatting safe onto safe with << yields safe" do 850 | # @other_string = "other".html_safe 851 | # string = @string.html_safe 852 | 853 | # @other_string << string 854 | # assert_predicate @other_string, :html_safe? 855 | # end 856 | 857 | # test "Concatting safe onto unsafe with % yields unsafe" do 858 | # @other_string = "other%s" 859 | # string = @string.html_safe 860 | 861 | # @other_string = @other_string % string 862 | # assert_not_predicate @other_string, :html_safe? 863 | # end 864 | 865 | # test "Concatting unsafe onto safe with % yields escaped safe" do 866 | # @other_string = "other%s".html_safe 867 | # string = @other_string % "" 868 | 869 | # assert_equal "other<foo>", string 870 | # assert_predicate string, :html_safe? 871 | # end 872 | 873 | # test "Concatting safe onto safe with % yields safe" do 874 | # @other_string = "other%s".html_safe 875 | # string = @string.html_safe 876 | 877 | # @other_string = @other_string % string 878 | # assert_predicate @other_string, :html_safe? 879 | # end 880 | 881 | # test "Concatting with % doesn't modify a string" do 882 | # @other_string = ["

", "", "

"] 883 | # _ = "%s %s %s".html_safe % @other_string 884 | 885 | # assert_equal ["

", "", "

"], @other_string 886 | # end 887 | 888 | # test "Concatting an integer to safe always yields safe" do 889 | # string = @string.html_safe 890 | # string = string.concat(13) 891 | # assert_equal "hello".dup.concat(13), string 892 | # assert_predicate string, :html_safe? 893 | # end 894 | 895 | # test "emits normal string yaml" do 896 | # assert_equal "foo".to_yaml, "foo".html_safe.to_yaml(foo: 1) 897 | # end 898 | 899 | # test "call to_param returns a normal string" do 900 | # string = @string.html_safe 901 | # assert_predicate string, :html_safe? 902 | # assert_not_predicate string.to_param, :html_safe? 903 | # end 904 | 905 | # test "ERB::Util.html_escape should escape unsafe characters" do 906 | # string = '<>&"\'' 907 | # expected = "<>&"'" 908 | # assert_equal expected, ERB::Util.html_escape(string) 909 | # end 910 | 911 | # test "ERB::Util.html_escape should correctly handle invalid UTF-8 strings" do 912 | # string = "\251 <" 913 | # expected = "© <" 914 | # assert_equal expected, ERB::Util.html_escape(string) 915 | # end 916 | 917 | # test "ERB::Util.html_escape should not escape safe strings" do 918 | # string = "hello".html_safe 919 | # assert_equal string, ERB::Util.html_escape(string) 920 | # end 921 | 922 | # test "ERB::Util.html_escape_once only escapes once" do 923 | # string = "1 < 2 & 3" 924 | # escaped_string = "1 < 2 & 3" 925 | 926 | # assert_equal escaped_string, ERB::Util.html_escape_once(string) 927 | # assert_equal escaped_string, ERB::Util.html_escape_once(escaped_string) 928 | # end 929 | 930 | # test "ERB::Util.html_escape_once should correctly handle invalid UTF-8 strings" do 931 | # string = "\251 <" 932 | # expected = "© <" 933 | # assert_equal expected, ERB::Util.html_escape_once(string) 934 | # end 935 | # end 936 | 937 | # class StringExcludeTest < ActiveSupport::TestCase 938 | # test "inverse of #include" do 939 | # assert_equal false, "foo".exclude?("o") 940 | # assert_equal true, "foo".exclude?("p") 941 | # end 942 | # end 943 | 944 | # class StringIndentTest < ActiveSupport::TestCase 945 | # test "does not indent strings that only contain newlines (edge cases)" do 946 | # ["", "\n", "\n" * 7].each do |string| 947 | # str = string.dup 948 | # assert_nil str.indent!(8) 949 | # assert_equal str, str.indent(8) 950 | # assert_equal str, str.indent(1, "\t") 951 | # end 952 | # end 953 | 954 | # test "by default, indents with spaces if the existing indentation uses them" do 955 | # assert_equal " foo\n bar", "foo\n bar".indent(4) 956 | # end 957 | 958 | # test "by default, indents with tabs if the existing indentation uses them" do 959 | # assert_equal "\tfoo\n\t\t\bar", "foo\n\t\bar".indent(1) 960 | # end 961 | 962 | # test "by default, indents with spaces as a fallback if there is no indentation" do 963 | # assert_equal " foo\n bar\n baz", "foo\nbar\nbaz".indent(3) 964 | # end 965 | 966 | # # Nothing is said about existing indentation that mixes spaces and tabs, so 967 | # # there is nothing to test. 968 | 969 | # test "uses the indent char if passed" do 970 | # assert_equal <