opal-activesupport 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+