opal 0.8.0.beta1 → 0.8.0.rc1

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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +0 -24
  3. data/.jshintrc +51 -0
  4. data/.travis.yml +6 -1
  5. data/CHANGELOG.md +2 -0
  6. data/Gemfile +3 -7
  7. data/README.md +2 -3
  8. data/Rakefile +1 -0
  9. data/lib/mspec/opal/rake_task.rb +1 -1
  10. data/lib/opal/builder.rb +21 -3
  11. data/lib/opal/builder_processors.rb +2 -2
  12. data/lib/opal/cli.rb +8 -5
  13. data/lib/opal/erb.rb +1 -1
  14. data/lib/opal/nodes/call.rb +1 -1
  15. data/lib/opal/nodes/call_special.rb +1 -1
  16. data/lib/opal/nodes/helpers.rb +6 -4
  17. data/lib/opal/nodes/iter.rb +2 -2
  18. data/lib/opal/nodes/singleton_class.rb +2 -1
  19. data/lib/opal/parser/lexer.rb +3 -2
  20. data/lib/opal/path_reader.rb +8 -1
  21. data/lib/opal/paths.rb +23 -16
  22. data/lib/opal/regexp_anchors.rb +5 -0
  23. data/lib/opal/sprockets/erb.rb +1 -1
  24. data/lib/opal/sprockets/processor.rb +21 -35
  25. data/lib/opal/sprockets/source_map_server.rb +5 -5
  26. data/lib/opal/version.rb +3 -1
  27. data/lib/tilt/opal.rb +11 -5
  28. data/opal.gemspec +2 -2
  29. data/opal/corelib/array.rb +196 -4
  30. data/opal/corelib/enumerable.rb +1 -1
  31. data/opal/corelib/enumerator.rb +2 -2
  32. data/opal/corelib/hash.rb +6 -6
  33. data/opal/corelib/kernel.rb +5 -3
  34. data/opal/corelib/module.rb +18 -12
  35. data/opal/corelib/regexp.rb +110 -4
  36. data/opal/corelib/runtime.js +12 -4
  37. data/opal/corelib/string.rb +51 -95
  38. data/opal/corelib/string/inheritance.rb +22 -0
  39. data/opal/corelib/variables.rb +1 -1
  40. data/spec/filters/bugs/array.rb +3 -39
  41. data/spec/filters/bugs/basic_object.rb +20 -0
  42. data/spec/filters/bugs/date.rb +5 -0
  43. data/spec/filters/bugs/enumerable.rb +9 -0
  44. data/spec/filters/bugs/enumerator.rb +0 -7
  45. data/spec/filters/bugs/hash.rb +2 -0
  46. data/spec/filters/bugs/kernel.rb +5 -0
  47. data/spec/filters/bugs/language.rb +14 -0
  48. data/spec/filters/bugs/method.rb +5 -0
  49. data/spec/filters/bugs/module.rb +23 -0
  50. data/spec/filters/bugs/regular_expressions.rb +41 -0
  51. data/spec/filters/bugs/singleton.rb +3 -0
  52. data/spec/filters/bugs/string.rb +2 -9
  53. data/spec/filters/bugs/stringscanner.rb +3 -0
  54. data/spec/filters/bugs/time.rb +19 -0
  55. data/spec/filters/unsupported/module.rb +6 -0
  56. data/spec/filters/unsupported/mutable_strings.rb +8 -0
  57. data/spec/filters/unsupported/regular_expressions.rb +90 -0
  58. data/spec/lib/builder_spec.rb +12 -6
  59. data/spec/lib/cli_spec.rb +2 -2
  60. data/spec/lib/path_reader_spec.rb +12 -0
  61. data/spec/lib/sprockets/server_spec.rb +7 -8
  62. data/spec/lib/tilt/opal_spec.rb +18 -0
  63. data/spec/opal/core/language/regexp_spec.rb +1 -1
  64. data/spec/opal/core/runtime/bridged_classes_spec.rb +48 -1
  65. data/spec/rubyspecs +88 -78
  66. data/stdlib/encoding.rb +3 -0
  67. data/stdlib/native.rb +1 -1
  68. data/stdlib/pp.rb +27 -6
  69. data/stdlib/set.rb +10 -0
  70. data/tasks/linting.rake +18 -0
  71. data/tasks/testing.rake +11 -4
  72. data/{stdlib → vendored-minitest}/minitest.rb +0 -0
  73. data/{stdlib → vendored-minitest}/minitest/assertions.rb +0 -0
  74. data/{stdlib → vendored-minitest}/minitest/autorun.rb +0 -0
  75. data/{stdlib → vendored-minitest}/minitest/benchmark.rb +0 -0
  76. data/{stdlib → vendored-minitest}/minitest/expectations.rb +0 -0
  77. data/{stdlib → vendored-minitest}/minitest/hell.rb +0 -0
  78. data/{stdlib → vendored-minitest}/minitest/mock.rb +0 -0
  79. data/{stdlib → vendored-minitest}/minitest/parallel.rb +0 -0
  80. data/{stdlib → vendored-minitest}/minitest/pride.rb +0 -0
  81. data/{stdlib → vendored-minitest}/minitest/pride_plugin.rb +0 -0
  82. data/{stdlib → vendored-minitest}/minitest/spec.rb +0 -0
  83. data/{stdlib → vendored-minitest}/minitest/test.rb +0 -0
  84. data/{stdlib → vendored-minitest}/minitest/unit.rb +0 -0
  85. data/vendored-minitest/test/unit.rb +23 -0
  86. metadata +31 -29
  87. data/stdlib/test/unit.rb +0 -10
  88. data/tasks/documentation.rake +0 -38
@@ -0,0 +1,5 @@
1
+ module Opal
2
+ REGEXP_START = RUBY_ENGINE == 'opal' ? '^' : '\A'.freeze
3
+ REGEXP_END = RUBY_ENGINE == 'opal' ? '$' : '\z'.freeze
4
+ end
5
+
@@ -11,7 +11,7 @@ module Opal
11
11
  end
12
12
 
13
13
  def evaluate(context, locals, &block)
14
- compiler = Opal::ERB::Compiler.new(@data, context.logical_path.sub(/^templates\//, ''))
14
+ compiler = Opal::ERB::Compiler.new(@data, context.logical_path.sub(/#{REGEXP_START}templates\//, ''))
15
15
  @data = compiler.prepared_source
16
16
  super
17
17
  end
@@ -27,7 +27,7 @@ module Opal
27
27
  # In Sprockets 3 logical_path has an odd behavior when the filename is "index"
28
28
  # thus we need to bake our own logical_path
29
29
  filename = context.respond_to?(:filename) ? context.filename : context.pathname.to_s
30
- logical_path = filename.sub(%r{^#{context.root_path}/?(.*?)#{sprockets_extnames_regexp}}, '\1')
30
+ logical_path = filename.gsub(%r{^#{context.root_path}/?(.*?)#{sprockets_extnames_regexp}}, '\1')
31
31
 
32
32
  compiler_options = self.compiler_options.merge(file: logical_path)
33
33
 
@@ -54,7 +54,7 @@ module Opal
54
54
  end
55
55
 
56
56
  def self.sprockets_extnames_regexp(sprockets)
57
- joined_extnames = sprockets.engines.keys.map { |ext| Regexp.escape(ext) }.join('|')
57
+ joined_extnames = (['.js']+sprockets.engines.keys).map { |ext| Regexp.escape(ext) }.join('|')
58
58
  Regexp.new("(#{joined_extnames})*$")
59
59
  end
60
60
 
@@ -75,7 +75,7 @@ module Opal
75
75
 
76
76
  # This is the root dir of the logical path, we need this because
77
77
  # the compiler gives us the path relative to the file's logical path.
78
- dirname = File.dirname(file).gsub(/#{Regexp.escape File.dirname(context.logical_path)}$/, '')
78
+ dirname = File.dirname(file).gsub(/#{Regexp.escape File.dirname(context.logical_path)}#{REGEXP_END}/, '')
79
79
  dirname = Pathname(dirname)
80
80
 
81
81
  required_trees.each do |original_required_tree|
@@ -95,50 +95,36 @@ module Opal
95
95
 
96
96
  environment = context.environment
97
97
 
98
- if environment.respond_to?(:each_entry)
99
- # Sprockets 2
100
- environment.each_entry(required_tree) do |pathname|
101
- if pathname.to_s == file
102
- next
103
- elsif pathname.directory?
104
- context.depend_on(pathname)
105
- elsif context.asset_requirable?(pathname)
106
- context.require_asset(pathname)
107
- end
108
- end
109
- else
110
- # Sprockets 3
111
- processor = ::Sprockets::DirectiveProcessor.new
112
- processor.instance_variable_set('@dirname', File.dirname(file))
113
- processor.instance_variable_set('@environment', environment)
114
- path = processor.__send__(:expand_relative_dirname, :require_tree, original_required_tree)
115
- absolute_paths = environment.__send__(:stat_sorted_tree_with_dependencies, path).first.map(&:first)
116
-
117
- absolute_paths.each do |path|
118
- path = Pathname(path)
119
- pathname = path.relative_path_from(dirname)
120
-
121
- if name.to_s == file
122
- next
123
- elsif path.directory?
124
- context.depend_on(path.to_s)
125
- else
126
- context.require_asset(pathname)
127
- end
98
+ processor = ::Sprockets::DirectiveProcessor.new
99
+ processor.instance_variable_set('@dirname', File.dirname(file))
100
+ processor.instance_variable_set('@environment', environment)
101
+ path = processor.__send__(:expand_relative_dirname, :require_tree, original_required_tree)
102
+ absolute_paths = environment.__send__(:stat_sorted_tree_with_dependencies, path).first.map(&:first)
103
+
104
+ absolute_paths.each do |path|
105
+ path = Pathname(path)
106
+ pathname = path.relative_path_from(dirname)
107
+
108
+ if name.to_s == file
109
+ next
110
+ elsif path.directory?
111
+ context.depend_on(path.to_s)
112
+ else
113
+ context.require_asset(pathname)
128
114
  end
129
115
  end
130
116
  end
131
117
  end
132
118
 
133
119
  def self.load_asset_code(sprockets, name)
134
- asset = sprockets[name.sub(/(\.(js|rb|opal))*$/, '.js')]
120
+ asset = sprockets[name.sub(/(\.(js|rb|opal))*#{REGEXP_END}/, '.js')]
135
121
  return '' if asset.nil?
136
122
 
137
123
  opal_extnames = sprockets.engines.map do |ext, engine|
138
124
  ext if engine <= ::Opal::Processor
139
125
  end.compact
140
126
 
141
- module_name = -> asset { asset.logical_path.sub(/\.js$/, '') }
127
+ module_name = -> asset { asset.logical_path.sub(/\.js#{REGEXP_END}/, '') }
142
128
  path_extnames = -> path { File.basename(path).scan(/\.[^.]+/) }
143
129
  mark_as_loaded = -> path { "Opal.mark_as_loaded(#{path.inspect});" }
144
130
  processed_by_opal = -> asset { (path_extnames[asset.pathname] & opal_extnames).any? }
@@ -45,13 +45,13 @@ module Opal
45
45
  end
46
46
 
47
47
  def self.get_map_cache(sprockets, logical_path)
48
- logical_path = logical_path.gsub(/\.js$/, '')
48
+ logical_path = logical_path.gsub(/\.js#{REGEXP_END}/, '')
49
49
  cache_key = cache_key_for_path(logical_path+'.map')
50
50
  cache(sprockets).cache_get(cache_key)
51
51
  end
52
52
 
53
53
  def self.set_map_cache(sprockets, logical_path, map_contents)
54
- logical_path = logical_path.gsub(/\.js$/, '')
54
+ logical_path = logical_path.gsub(/\.js#{REGEXP_END}/, '')
55
55
  cache_key = cache_key_for_path(logical_path+'.map')
56
56
  cache(sprockets).cache_set(cache_key, map_contents)
57
57
  end
@@ -61,7 +61,7 @@ module Opal
61
61
  end
62
62
 
63
63
  def self.cache_key_for_path(logical_path)
64
- base_name = logical_path.gsub(/\.js$/, '')
64
+ base_name = logical_path.gsub(/\.js#{REGEXP_END}/, '')
65
65
  File.join('opal', 'source_maps', base_name)
66
66
  end
67
67
 
@@ -82,7 +82,7 @@ module Opal
82
82
  path_info = env['PATH_INFO'].to_s.sub(prefix_regex, '')
83
83
 
84
84
  case path_info
85
- when %r{^(.*)\.map$}
85
+ when %r{^(.*)\.self\.map$}
86
86
  path = $1
87
87
  asset = sprockets[path]
88
88
  return not_found(path) if asset.nil?
@@ -98,7 +98,7 @@ module Opal
98
98
  return [200, {"Content-Type" => "text/json"}, [source.to_s]]
99
99
  when %r{^(.*)\.rb$}
100
100
  begin
101
- asset = sprockets.resolve(path_info.sub(/\.rb$/,''))
101
+ asset = sprockets.resolve(path_info.sub(/\.rb#{REGEXP_END}/,''))
102
102
  rescue Sprockets::FileNotFound
103
103
  return not_found(path_info)
104
104
  end
data/lib/opal/version.rb CHANGED
@@ -1,3 +1,5 @@
1
1
  module Opal
2
- VERSION = '0.8.0.beta1'
2
+ # WHEN RELEASING:
3
+ # Remember to update RUBY_ENGINE_VERSION in opal/corelib/variables.rb too!
4
+ VERSION = '0.8.0.rc1'
3
5
  end
data/lib/tilt/opal.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'tilt'
2
- require 'opal/compiler'
2
+ require 'opal/builder'
3
3
  require 'opal/config'
4
4
  require 'opal/version'
5
5
 
@@ -32,10 +32,16 @@ module Opal
32
32
  def prepare
33
33
  end
34
34
 
35
- def evaluate(context, locals, &block)
36
- compiler_options = Opal::Config.compiler_options.merge(file: file)
37
- compiler = Compiler.new(data, compiler_options)
38
- compiler.compile.to_s
35
+ def evaluate(_, _, &block)
36
+ if builder = @options[:builder]
37
+ builder.dup.build(file).to_s
38
+ elsif @options[:build]
39
+ Opal::Builder.build(file).to_s
40
+ else
41
+ compiler_options = (compiler_options || {}).merge!(file: file)
42
+ compiler = Compiler.new(data, compiler_options)
43
+ compiler.compile.to_s
44
+ end
39
45
  end
40
46
 
41
47
  def compiler_options
data/opal.gemspec CHANGED
@@ -21,9 +21,9 @@ Gem::Specification.new do |s|
21
21
  required_ruby_version = '>= 1.9.3'
22
22
 
23
23
  s.add_dependency 'sourcemap', '~> 0.1.0'
24
- s.add_dependency 'sprockets', '>= 2.2.3', '< 4.0.0'
24
+ s.add_dependency 'sprockets', '~> 3.1'
25
25
  s.add_dependency 'hike', '~> 1.2'
26
- s.add_dependency 'tilt', '~> 1.4'
26
+ s.add_dependency 'tilt', '>= 1.4'
27
27
 
28
28
  s.add_development_dependency 'mspec', '1.5.20'
29
29
  s.add_development_dependency 'rake'
@@ -472,6 +472,48 @@ class Array
472
472
  }
473
473
  end
474
474
 
475
+ def bsearch(&block)
476
+ return enum_for :bsearch unless block_given?
477
+
478
+ %x{
479
+ var min = 0,
480
+ max = self.length,
481
+ mid,
482
+ val,
483
+ ret,
484
+ smaller = false,
485
+ satisfied = nil;
486
+
487
+ while (min < max) {
488
+ mid = min + Math.floor((max - min) / 2);
489
+ val = self[mid];
490
+ ret = block(val);
491
+
492
+ if (ret === $breaker) {
493
+ return $breaker.$v;
494
+ }
495
+ else if (ret === true) {
496
+ satisfied = val;
497
+ smaller = true;
498
+ }
499
+ else if (ret === false || ret === nil) {
500
+ smaller = false;
501
+ }
502
+ else if (ret.$$is_number) {
503
+ if (ret === 0) { return val; }
504
+ smaller = (ret < 0);
505
+ }
506
+ else {
507
+ #{raise TypeError, "wrong argument type #{`ret`.class} (must be numeric, true, false or nil)"}
508
+ }
509
+
510
+ if (smaller) { max = mid; } else { min = mid + 1; }
511
+ }
512
+
513
+ return satisfied;
514
+ }
515
+ end
516
+
475
517
  def cycle(n = nil, &block)
476
518
  return if empty? || n == 0
477
519
 
@@ -574,6 +616,54 @@ class Array
574
616
  self
575
617
  end
576
618
 
619
+ def combination(n)
620
+ num = Opal.coerce_to! n, Integer, :to_int
621
+ return enum_for :combination, num unless block_given?
622
+
623
+ %x{
624
+ var i, length, stack, chosen, lev, done, next;
625
+
626
+ if (num === 0) {
627
+ #{yield []}
628
+ } else if (num === 1) {
629
+ for (i = 0, length = self.length; i < length; i++) {
630
+ #{yield `[self[i]]`}
631
+ }
632
+ }
633
+ else if (num === self.length) {
634
+ #{yield `self.slice()`}
635
+ }
636
+ else if (num >= 0 && num < self.length) {
637
+ stack = [];
638
+ for (i = 0; i <= num + 1; i++) {
639
+ stack.push(0);
640
+ }
641
+
642
+ chosen = [];
643
+ lev = 0;
644
+ done = false;
645
+ stack[0] = -1;
646
+
647
+ while (!done) {
648
+ chosen[lev] = self[stack[lev+1]];
649
+ while (lev < num - 1) {
650
+ lev++;
651
+ next = stack[lev+1] = stack[lev] + 1;
652
+ chosen[lev] = self[next];
653
+ }
654
+ #{ yield `chosen.slice()` }
655
+ lev++;
656
+ do {
657
+ done = (lev === 0);
658
+ stack[lev]++;
659
+ lev--;
660
+ } while ( stack[lev+1] + num === self.length + lev + 1 );
661
+ }
662
+ }
663
+ }
664
+ self
665
+ end
666
+
577
667
  def compact
578
668
  %x{
579
669
  var result = [];
@@ -1379,7 +1469,41 @@ class Array
1379
1469
  return nil;
1380
1470
  }
1381
1471
  end
1472
+
1473
+ def rotate(n=1)
1474
+ n = Opal.coerce_to n, Integer, :to_int
1475
+ %x{
1476
+ var ary, idx, firstPart, lastPart;
1477
+
1478
+ if (self.length === 1) {
1479
+ return self.slice();
1480
+ }
1481
+ if (self.length === 0) {
1482
+ return [];
1483
+ }
1484
+
1485
+ ary = self.slice();
1486
+ idx = n % ary.length;
1487
+
1488
+ firstPart = ary.slice(idx);
1489
+ lastPart = ary.slice(0, idx);
1490
+ return firstPart.concat(lastPart);
1491
+ }
1492
+ end
1493
+
1494
+ def rotate!(cnt=1)
1495
+ raise RuntimeError, "can't modify frozen Array" if frozen?
1382
1496
 
1497
+ %x{
1498
+ if (self.length === 0 || self.length === 1) {
1499
+ return self;
1500
+ }
1501
+ }
1502
+ cnt = Opal.coerce_to cnt, Integer, :to_int
1503
+ ary = rotate(cnt)
1504
+ replace ary
1505
+ end
1506
+
1383
1507
  def sample(n = nil)
1384
1508
  return nil if !n && empty?
1385
1509
  return [] if n && empty?
@@ -1575,6 +1699,27 @@ class Array
1575
1699
 
1576
1700
  alias to_ary to_a
1577
1701
 
1702
+ def to_h
1703
+ %x{
1704
+ var i, len = self.length, ary, key, val, hash = #{{}};
1705
+
1706
+ for (i = 0; i < len; i++) {
1707
+ ary = #{Opal.coerce_to?(`self[i]`, Array, :to_ary)};
1708
+ if (!ary.$$is_array) {
1709
+ #{raise TypeError, "wrong element type #{`ary`.class} at #{`i`} (expected array)"}
1710
+ }
1711
+ if (ary.length !== 2) {
1712
+ #{raise ArgumentError, "wrong array length at #{`i`} (expected 2, was #{`ary`.length})"}
1713
+ }
1714
+ key = ary[0];
1715
+ val = ary[1];
1716
+ hash.$store(key, val);
1717
+ }
1718
+
1719
+ return hash;
1720
+ }
1721
+ end
1722
+
1578
1723
  alias to_s inspect
1579
1724
 
1580
1725
  def transpose
@@ -1659,14 +1804,61 @@ class Array
1659
1804
  self
1660
1805
  end
1661
1806
 
1807
+ def values_at(*args)
1808
+ out = [];
1809
+
1810
+ args.each do |elem|
1811
+ if elem.kind_of? Range
1812
+ finish = Opal.coerce_to elem.last, Integer, :to_int
1813
+ start = Opal.coerce_to elem.first, Integer, :to_int
1814
+
1815
+ %x{
1816
+ if (start < 0) {
1817
+ start = start + self.length;
1818
+ #{next};
1819
+ }
1820
+ }
1821
+
1822
+ %x{
1823
+ if (finish < 0) {
1824
+ finish = finish + self.length;
1825
+ }
1826
+ if (#{elem.exclude_end?}) {
1827
+ finish--;
1828
+ }
1829
+ if (finish < start) {
1830
+ #{next};
1831
+ }
1832
+ }
1833
+
1834
+ start.upto(finish) { |i| out << at(i) }
1835
+ else
1836
+ i = Opal.coerce_to elem, Integer, :to_int
1837
+ out << at(i)
1838
+ end
1839
+ end
1840
+
1841
+ out
1842
+ end
1843
+
1662
1844
  def zip(*others, &block)
1663
1845
  %x{
1664
- var result = [], size = self.length, part, o;
1846
+ var result = [], size = self.length, part, o, i, j, jj;
1665
1847
 
1666
- for (var i = 0; i < size; i++) {
1848
+ for (j = 0, jj = others.length; j < jj; j++) {
1849
+ o = others[j];
1850
+ if (!o.$$is_array) {
1851
+ others[j] = #{(
1852
+ Opal.coerce_to?(`o`, Array, :to_ary) ||
1853
+ Opal.coerce_to!(`o`, Enumerator, :each)
1854
+ ).to_a};
1855
+ }
1856
+ }
1857
+
1858
+ for (i = 0; i < size; i++) {
1667
1859
  part = [self[i]];
1668
1860
 
1669
- for (var j = 0, jj = others.length; j < jj; j++) {
1861
+ for (j = 0, jj = others.length; j < jj; j++) {
1670
1862
  o = others[j][i];
1671
1863
 
1672
1864
  if (o == null) {
@@ -1680,7 +1872,7 @@ class Array
1680
1872
  }
1681
1873
 
1682
1874
  if (block !== nil) {
1683
- for (var i = 0; i < size; i++) {
1875
+ for (i = 0; i < size; i++) {
1684
1876
  block(result[i]);
1685
1877
  }
1686
1878