opal-activesupport 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab212ff0639ad0d73aa14079bee64d887be2a76cda925d7b06f714f0c6dbca83
4
- data.tar.gz: 85681bc3a9c4baaf2e8ef296e7b78e0bed6c517e1d9799a86f446970dbb90a39
3
+ metadata.gz: 7b194f0cee109c7a300653dc94711e9a9ce0b2d5d99e83f6ef48c23b3e126c48
4
+ data.tar.gz: 32d323abd0065e8b6f3c577959403f27cc630e11c9ffa3e69991f1b7d6b6b30e
5
5
  SHA512:
6
- metadata.gz: c95184d0f80dfd759498b94be051c527eec9bd918d72f37e3fbe493df5fc8c417ce4a5659afa0d55208d3bec0be85ec7210efce55715d4440f6763941d285a28
7
- data.tar.gz: aa831820632011e8ff2f6879fc5a2d8073da35e96d2a904d73ffc9162089ad1ad2b1ddcef3e24ea5e2ac8fb80e28b8e2a6ef3b1c4540c50f5980632e7654e8b4
6
+ metadata.gz: 80fdb487f5bc4ccedd111843959084276649820c8f308871b54c005049589aa26c78b975674c2cecb44fbc627931d3852e5fe11696d4a2868e3f7657b8113b14
7
+ data.tar.gz: 1d08de6d0ae15c2f2c043f72e3941e8e9355f53c3e476c1947d21001ee273a9dd4563fd71dbdba51f91cb5442e6511b5191cd7fecd37d0b29e1688f1825e5d53
@@ -0,0 +1,47 @@
1
+ ### 0.3.2 - unreleased
2
+
3
+ [Changes](https://github.com/opal/opal-activesupport/compare/v0.3.2...HEAD)
4
+
5
+ ### 0.3.2 - 2019-04-27
6
+
7
+ [Changes](https://github.com/opal/opal-activesupport/compare/v0.3.1...v0.3.2)
8
+
9
+ - Added `ActiveSupport::Concern` (#19).
10
+ - Added `String#truncate` (#22).
11
+ - Properly backported: (#21).
12
+ + `ActiveSupport::Inflector.pluralize` and `String#pluralize`
13
+ + `ActiveSupport::Inflector.singularize` and `String#singularize`
14
+ + `ActiveSupport::Inflector.constantize` and `String#constantize`
15
+ + `ActiveSupport::Inflector.safe_constantize` and `String#safe_constantize`
16
+ + `ActiveSupport::Inflector.camelize` and `String#camelize`
17
+ + `ActiveSupport::Inflector.titleize` and `String#titleize`
18
+ + `ActiveSupport::Inflector.underscore` and `String#underscore`
19
+ + `ActiveSupport::Inflector.dasherize` and `String#dasherize`
20
+ + `ActiveSupport::Inflector.demodulize` and `String#demodulize`
21
+ + `ActiveSupport::Inflector.deconstantize` and `String#deconstantize`
22
+ + `ActiveSupport::Inflector.tableize` and `String#tableize`
23
+ + `ActiveSupport::Inflector.classify` and `String#classify`
24
+ + `ActiveSupport::Inflector.humanize` and `String#humanize`
25
+ + `ActiveSupport::Inflector.upcase_first` and `String#upcase_first`
26
+ + `ActiveSupport::Inflector.foreign_key` and `String#foreign_key`
27
+
28
+ ### 0.3.1 - 2018-01-28
29
+
30
+ [Changes](https://github.com/opal/opal-activesupport/compare/v0.3.0...v0.3.1)
31
+
32
+ - Fix `Inflections.irregular`
33
+ - Fix `Inflector.apply_inflections`
34
+ - Fix `Inflector.inflections`
35
+ - Fix `return` handling of x-string for Opal v0.11
36
+
37
+ ### 0.3.0 - 2015-12-23
38
+
39
+ [Changes](https://github.com/opal/opal-activesupport/compare/v0.2.0...v0.3.0)
40
+
41
+ ### 0.2.0 - 2015-10-08
42
+
43
+ [Changes](https://github.com/opal/opal-activesupport/compare/v0.1.0...v0.2.0)
44
+
45
+ ### 0.1.0 - 2015-02-03
46
+
47
+ _the fogs of the past 🌫_
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  # Opal: ActiveSupport
2
2
 
3
- _@dhh [6:44 PM - Oct 23, 2012](https://twitter.com/dhh/status/260783823254601728):_
3
+ [![Build Status](https://travis-ci.org/opal/opal-activesupport.svg?branch=master)](https://travis-ci.org/opal/opal-activesupport)
4
+
4
5
  > @AstonJ But it's vanilla Ruby. It's not like you have ActiveSupport available, which somewhat defeats it for me.
5
6
 
7
+ _[@dhh 6:44 PM - Oct 23, 2012](https://twitter.com/dhh/status/260783823254601728)_
8
+
6
9
 
7
10
  ## Installation
8
11
 
data/Rakefile CHANGED
@@ -1,15 +1,31 @@
1
1
  require 'bundler'
2
2
  Bundler.require
3
+ require 'bundler/gem_tasks'
3
4
 
4
- require 'opal/minitest/rake_task'
5
- # Opal::Minitest::RakeTask.new
6
5
 
7
6
  task :test do
7
+ require 'opal'
8
+ require 'opal/cli_runners'
9
+ require 'opal/minitest'
10
+
8
11
  Opal::Config.arity_check_enabled = true
9
- files = Dir['test/**/*_test.rb'].map {|f| "-r #{f.chomp('.rb').sub(/^test\//, '')}"}
10
- sh "bundle exec ruby -r opal/minitest -S opal -Dwarning -Itest -Iopal #{files.join(' ')} -e puts"
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, [])
11
29
  end
12
30
 
13
31
  task default: :test
14
-
15
- require 'bundler/gem_tasks'
@@ -1,5 +1,5 @@
1
1
  module Opal
2
2
  module Activesupport
3
- VERSION = '0.3.1'
3
+ VERSION = '0.3.2'
4
4
  end
5
5
  end
@@ -22,5 +22,6 @@ Gem::Specification.new do |gem|
22
22
 
23
23
  gem.add_dependency 'opal', ['>= 0.5.0', '< 1.0.0']
24
24
  gem.add_development_dependency 'opal-minitest'
25
+ gem.add_development_dependency 'opal-sprockets'
25
26
  gem.add_development_dependency 'rake'
26
27
  end
@@ -0,0 +1,152 @@
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 <tt>ActiveSupport::Concern</tt> 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
@@ -1,45 +1,8 @@
1
+ require 'active_support/core_ext/string/filters'
1
2
  require 'active_support/core_ext/string/inflections'
2
3
 
3
4
  class String
4
5
  def parameterize
5
6
  self.downcase.strip.gsub(/\W+/, '-')
6
7
  end
7
-
8
- def dasherize
9
- result = `#{self}.replace(/[-_\s]+/g, '-')
10
- .replace(/([A-Z\d]+)([A-Z][a-z])/g, '$1-$2')
11
- .replace(/([a-z\d])([A-Z])/g, '$1-$2')
12
- .toLowerCase()`
13
- result
14
- end
15
-
16
- def demodulize
17
- %x{
18
- var idx = #{self}.lastIndexOf('::');
19
-
20
- if (idx > -1) {
21
- return #{self}.substr(idx + 2);
22
- }
23
-
24
- return #{self};
25
- }
26
- end
27
-
28
- def underscore
29
- result = `#{self}.replace(/[-\s]+/g, '_')
30
- .replace(/([A-Z\d]+)([A-Z][a-z])/g, '$1_$2')
31
- .replace(/([a-z\d])([A-Z])/g, '$1_$2')
32
- .replace(/-/g, '_')
33
- .toLowerCase()`
34
- result
35
- end
36
-
37
- def camelize(first_letter = :upper)
38
- result = `#{underscore}.replace(/(^|_)([^_]+)/g, function(match, pre, word, index) {
39
- var capitalize = #{first_letter} === #{:upper} || index > 0;
40
- return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
41
- })`
42
- result
43
- end
44
- alias_method :camelcase, :camelize
45
8
  end
@@ -0,0 +1,146 @@
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 <tt>length</tt> if +text+ is longer than <tt>length</tt>:
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 <tt>:separator</tt> 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 <tt>:omission</tt> string (defaults to "...")
62
+ # for a total length not exceeding <tt>length</tt>:
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 <tt>bytesize</tt> 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 <tt>:omission</tt> string, defaulting
94
+ # to "…", for a total length not exceeding <tt>bytesize</tt>.
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 (<tt>words_count</tt>):
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 <tt>:separator</tt> to specify a different separator of words:
128
+ #
129
+ # 'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')
130
+ # # => "Once<br>upon<br>a<br>time<br>in..."
131
+ #
132
+ # The last characters will be replaced with the <tt>:omission</tt> 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
+