redstorm 0.6.4 → 0.6.5

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 (43) hide show
  1. data/CHANGELOG.md +9 -0
  2. data/README.md +206 -103
  3. data/examples/native/cluster_word_count_topology.rb +5 -5
  4. data/examples/native/local_exclamation_topology.rb +8 -8
  5. data/examples/native/local_exclamation_topology2.rb +7 -7
  6. data/examples/native/local_redis_word_count_topology.rb +7 -8
  7. data/examples/native/local_word_count_topology.rb +5 -5
  8. data/examples/simple/exclamation_topology.rb +7 -11
  9. data/examples/simple/exclamation_topology2.rb +10 -12
  10. data/examples/simple/hello_world_topology.rb +22 -0
  11. data/examples/simple/kafka_topology.rb +2 -1
  12. data/examples/simple/redis_word_count_topology.rb +3 -5
  13. data/examples/simple/ruby_version_topology.rb +7 -1
  14. data/examples/simple/word_count_topology.rb +8 -10
  15. data/ivy/settings.xml +1 -0
  16. data/ivy/storm_dependencies.xml +8 -0
  17. data/ivy/topology_dependencies.xml +7 -0
  18. data/lib/red_storm.rb +1 -0
  19. data/lib/red_storm/application.rb +9 -7
  20. data/lib/red_storm/configurator.rb +1 -1
  21. data/lib/red_storm/proxy/batch_bolt.rb +63 -0
  22. data/lib/red_storm/proxy/batch_committer_bolt.rb +52 -0
  23. data/lib/red_storm/proxy/batch_spout.rb +59 -0
  24. data/lib/red_storm/proxy/proxy_function.rb +40 -0
  25. data/lib/red_storm/proxy/transactional_committer_spout.rb +47 -0
  26. data/lib/red_storm/proxy/transactional_spout.rb +46 -0
  27. data/lib/red_storm/simple_drpc_topology.rb +87 -0
  28. data/lib/red_storm/simple_topology.rb +14 -4
  29. data/lib/red_storm/topology_launcher.rb +22 -3
  30. data/lib/red_storm/version.rb +1 -1
  31. data/lib/tasks/red_storm.rake +66 -104
  32. data/redstorm.gemspec +24 -0
  33. data/src/main/redstorm/storm/jruby/JRubyBatchBolt.java +90 -0
  34. data/src/main/redstorm/storm/jruby/JRubyBatchCommitterBolt.java +9 -0
  35. data/src/main/redstorm/storm/jruby/JRubyBatchSpout.java +88 -0
  36. data/src/main/redstorm/storm/jruby/JRubyProxyFunction.java +51 -0
  37. data/src/main/redstorm/storm/jruby/JRubyShellSpout.java +1 -1
  38. data/src/main/redstorm/storm/jruby/JRubyTransactionalBolt.java +90 -0
  39. data/src/main/redstorm/storm/jruby/JRubyTransactionalCommitterBolt.java +31 -0
  40. data/src/main/redstorm/storm/jruby/JRubyTransactionalCommitterSpout.java +44 -0
  41. data/src/main/redstorm/storm/jruby/JRubyTransactionalSpout.java +89 -0
  42. metadata +35 -14
  43. data/examples/native/Gemfile +0 -2
@@ -10,16 +10,16 @@ module RedStorm
10
10
 
11
11
  def start(base_class_path, env)
12
12
  builder = TopologyBuilder.new
13
- builder.setSpout('RandomSentenceSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RandomSentenceSpout"), 5)
14
- builder.setBolt('SplitSentenceBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::SplitSentenceBolt"), 4).shuffleGrouping('RandomSentenceSpout')
15
- builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt"), 4).fieldsGrouping('SplitSentenceBolt', Fields.new("word"))
13
+ builder.setSpout('RandomSentenceSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RandomSentenceSpout", []), 1)
14
+ builder.setBolt('SplitSentenceBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::SplitSentenceBolt", []), 2).shuffleGrouping('RandomSentenceSpout')
15
+ builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt", []), 2).fieldsGrouping('SplitSentenceBolt', Fields.new("word"))
16
16
 
17
17
  conf = Backtype::Config.new
18
18
  conf.setDebug(true)
19
- conf.setNumWorkers(20);
19
+ conf.setNumWorkers(4);
20
20
  conf.setMaxSpoutPending(1000);
21
21
  StormSubmitter.submitTopology("word_count", conf, builder.createTopology);
22
22
  end
23
23
  end
24
24
  end
25
- end
25
+ end
@@ -1,6 +1,6 @@
1
1
  java_import 'backtype.storm.testing.TestWordSpout'
2
2
 
3
- require 'lib/red_storm'
3
+ require 'red_storm'
4
4
  require 'examples/native/exclamation_bolt'
5
5
 
6
6
  # this example topology uses the Storm TestWordSpout and our own JRuby ExclamationBolt
@@ -12,14 +12,14 @@ module RedStorm
12
12
 
13
13
  def start(base_class_path, env)
14
14
  builder = TopologyBuilder.new
15
-
16
- builder.setSpout('TestWordSpout', TestWordSpout.new, 10)
17
- builder.setBolt('ExclamationBolt1', JRubyBolt.new(base_class_path, 'RedStorm::Examples::ExclamationBolt'), 3).shuffleGrouping('TestWordSpout')
18
- builder.setBolt('ExclamationBolt2', JRubyBolt.new(base_class_path, 'RedStorm::Examples::ExclamationBolt'), 3).shuffleGrouping('ExclamationBolt1')
19
-
15
+
16
+ builder.setSpout('TestWordSpout', TestWordSpout.new, 1)
17
+ builder.setBolt('ExclamationBolt1', JRubyBolt.new(base_class_path, 'RedStorm::Examples::ExclamationBolt', []), 2).shuffleGrouping('TestWordSpout')
18
+ builder.setBolt('ExclamationBolt2', JRubyBolt.new(base_class_path, 'RedStorm::Examples::ExclamationBolt', []), 2).shuffleGrouping('ExclamationBolt1')
19
+
20
20
  conf = Backtype::Config.new
21
21
  conf.setDebug(true)
22
-
22
+
23
23
  cluster = LocalCluster.new
24
24
  cluster.submitTopology("exclamation", conf, builder.createTopology)
25
25
  sleep(5)
@@ -28,4 +28,4 @@ module RedStorm
28
28
  end
29
29
  end
30
30
  end
31
- end
31
+ end
@@ -1,6 +1,6 @@
1
1
  java_import 'backtype.storm.testing.TestWordSpout'
2
2
 
3
- require 'lib/red_storm'
3
+ require 'red_storm'
4
4
 
5
5
  module RedStorm
6
6
  module Examples
@@ -29,14 +29,14 @@ module RedStorm
29
29
 
30
30
  def start(base_class_path, env)
31
31
  builder = TopologyBuilder.new
32
-
33
- builder.setSpout('TestWordSpout', TestWordSpout.new, 10)
34
- builder.setBolt('ExclamationBolt21', JRubyBolt.new(base_class_path, "RedStorm::Examples::ExclamationBolt2"), 3).shuffleGrouping('TestWordSpout')
35
- builder.setBolt('ExclamationBolt22', JRubyBolt.new(base_class_path, "RedStorm::Examples::ExclamationBolt2"), 2).shuffleGrouping('ExclamationBolt21')
36
-
32
+
33
+ builder.setSpout('TestWordSpout', TestWordSpout.new, 2)
34
+ builder.setBolt('ExclamationBolt21', JRubyBolt.new(base_class_path, "RedStorm::Examples::ExclamationBolt2", []), 2).shuffleGrouping('TestWordSpout')
35
+ builder.setBolt('ExclamationBolt22', JRubyBolt.new(base_class_path, "RedStorm::Examples::ExclamationBolt2", []), 2).shuffleGrouping('ExclamationBolt21')
36
+
37
37
  conf = Backtype::Config.new
38
38
  conf.setDebug(true)
39
-
39
+
40
40
  cluster = LocalCluster.new
41
41
  cluster.submitTopology("exclamation", conf, builder.createTopology)
42
42
  sleep(5)
@@ -1,12 +1,11 @@
1
- require 'bundler/setup'
1
+ require 'red_storm'
2
+ require 'examples/native/word_count_bolt'
2
3
  require 'redis'
3
4
  require 'thread'
4
- require 'lib/red_storm'
5
- require 'examples/native/word_count_bolt'
6
5
 
7
6
  module RedStorm
8
7
  module Examples
9
- # RedisWordSpout reads the Redis queue "test" on localhost:6379
8
+ # RedisWordSpout reads the Redis queue "test" on localhost:6379
10
9
  # and emits each word items pop'ed from the queue.
11
10
  class RedisWordSpout
12
11
  def open(conf, context, collector)
@@ -14,7 +13,7 @@ module RedStorm
14
13
  @q = Queue.new
15
14
  @redis_reader = detach_redis_reader
16
15
  end
17
-
16
+
18
17
  def next_tuple
19
18
  # per doc nextTuple should not block, and sleep a bit when there's no data to process.
20
19
  if @q.size > 0
@@ -52,8 +51,8 @@ module RedStorm
52
51
 
53
52
  def start(base_class_path, env)
54
53
  builder = TopologyBuilder.new
55
- builder.setSpout('RedisWordSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RedisWordSpout"), 1)
56
- builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt"), 3).fieldsGrouping('RedisWordSpout', Fields.new("word"))
54
+ builder.setSpout('RedisWordSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RedisWordSpout", []), 1)
55
+ builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt", []), 3).fieldsGrouping('RedisWordSpout', Fields.new("word"))
57
56
 
58
57
  conf = Backtype::Config.new
59
58
  conf.setDebug(true)
@@ -66,4 +65,4 @@ module RedStorm
66
65
  end
67
66
  end
68
67
  end
69
- end
68
+ end
@@ -1,4 +1,4 @@
1
- require 'lib/red_storm'
1
+ require 'red_storm'
2
2
  require 'examples/native/random_sentence_spout'
3
3
  require 'examples/native/split_sentence_bolt'
4
4
  require 'examples/native/word_count_bolt'
@@ -10,9 +10,9 @@ module Examples
10
10
 
11
11
  def start(base_class_path, env)
12
12
  builder = TopologyBuilder.new
13
- builder.setSpout('RandomSentenceSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RandomSentenceSpout"), 5)
14
- builder.setBolt('SplitSentenceBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::SplitSentenceBolt"), 8).shuffleGrouping('RandomSentenceSpout')
15
- builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt"), 12).fieldsGrouping('SplitSentenceBolt', Fields.new("word"))
13
+ builder.setSpout('RandomSentenceSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RandomSentenceSpout", []), 1)
14
+ builder.setBolt('SplitSentenceBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::SplitSentenceBolt", []), 2).shuffleGrouping('RandomSentenceSpout')
15
+ builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt", []), 2).fieldsGrouping('SplitSentenceBolt', Fields.new("word"))
16
16
 
17
17
  conf = Backtype::Config.new
18
18
  conf.setDebug(true)
@@ -24,4 +24,4 @@ module Examples
24
24
  cluster.shutdown
25
25
  end
26
26
  end
27
- end
27
+ end
@@ -1,36 +1,32 @@
1
1
  java_import 'backtype.storm.testing.TestWordSpout'
2
2
 
3
+ require 'red_storm'
3
4
  require 'examples/simple/exclamation_bolt'
4
5
 
5
6
  # this example topology uses the Storm TestWordSpout and our own JRuby ExclamationBolt
6
7
 
7
8
  module RedStorm
8
9
  module Examples
9
- class ExclamationTopology < RedStorm::SimpleTopology
10
- spout TestWordSpout, :parallelism => 5 do
10
+ class ExclamationTopology < SimpleTopology
11
+ spout TestWordSpout, :parallelism => 2 do
11
12
  debug true
12
13
  end
13
14
 
14
15
  bolt ExclamationBolt, :parallelism => 2 do
15
16
  source TestWordSpout, :shuffle
16
- # max_task_parallelism 1
17
17
  end
18
18
 
19
19
  bolt ExclamationBolt, :id => :ExclamationBolt2, :parallelism => 2 do
20
20
  source ExclamationBolt, :shuffle
21
- # max_task_parallelism 1
22
21
  debug true
23
22
  end
24
23
 
25
24
  configure do |env|
26
25
  debug false
27
- set "topology.worker.childopts", "-Djruby.compat.version=RUBY1_9"
28
- case env
29
- when :local
30
- max_task_parallelism 40
31
- when :cluster
32
- num_workers 20
33
- max_spout_pending(1000);
26
+ max_task_parallelism 4
27
+ if env == :cluster
28
+ num_workers 4
29
+ max_spout_pending(1000)
34
30
  end
35
31
  end
36
32
 
@@ -1,9 +1,10 @@
1
- java_import 'backtype.storm.testing.TestWordSpout'
2
- require 'red_storm'
3
-
4
1
  # this example topology uses the Storm TestWordSpout and our own JRuby ExclamationBolt
5
2
  # and a locally defined ExclamationBolt
6
3
 
4
+ java_import 'backtype.storm.testing.TestWordSpout'
5
+
6
+ require 'red_storm'
7
+
7
8
  module RedStorm
8
9
  module Examples
9
10
  class ExclamationBolt < RedStorm::SimpleBolt
@@ -12,9 +13,9 @@ module RedStorm
12
13
  end
13
14
 
14
15
  class ExclamationTopology2 < RedStorm::SimpleTopology
15
- spout TestWordSpout, :parallelism => 10
16
+ spout TestWordSpout, :parallelism => 2
16
17
 
17
- bolt ExclamationBolt, :parallelism => 3 do
18
+ bolt ExclamationBolt, :parallelism => 2 do
18
19
  source TestWordSpout, :shuffle
19
20
  end
20
21
 
@@ -24,13 +25,10 @@ module RedStorm
24
25
 
25
26
  configure do |env|
26
27
  debug true
27
- set "topology.worker.childopts", "-Djruby.compat.version=RUBY1_9"
28
- case env
29
- when :local
30
- max_task_parallelism 3
31
- when :cluster
32
- num_workers 20
33
- max_spout_pending(1000);
28
+ max_task_parallelism 4
29
+ if env == :cluster
30
+ num_workers 4
31
+ max_spout_pending(1000)
34
32
  end
35
33
  end
36
34
 
@@ -0,0 +1,22 @@
1
+ require 'red_storm'
2
+
3
+ class HelloWorldSpout < RedStorm::SimpleSpout
4
+ on_init {@words = ["hello", "world"]}
5
+ on_send {@words.shift unless @words.empty?}
6
+ end
7
+
8
+ class HelloWorldBolt < RedStorm::SimpleBolt
9
+ on_receive :emit => false do |tuple|
10
+ log.info(tuple.getString(0))
11
+ end
12
+ end
13
+
14
+ class HelloWorldTopology < RedStorm::SimpleTopology
15
+ spout HelloWorldSpout do
16
+ output_fields :word
17
+ end
18
+
19
+ bolt HelloWorldBolt do
20
+ source HelloWorldSpout, :global
21
+ end
22
+ end
@@ -1,9 +1,10 @@
1
- require 'red_storm'
2
1
  java_import 'storm.kafka.KafkaConfig'
3
2
  java_import 'storm.kafka.SpoutConfig'
4
3
  java_import 'storm.kafka.StringScheme'
5
4
  java_import 'storm.kafka.KafkaSpout'
6
5
 
6
+ require 'red_storm'
7
+
7
8
  # the KafkaTopology obviously requires a Kafka server running, you can ajust the
8
9
  # host and port below.
9
10
  #
@@ -1,9 +1,7 @@
1
- require 'rubygems'
2
1
  require 'red_storm'
3
-
2
+ require 'examples/simple/word_count_bolt'
4
3
  require 'redis'
5
4
  require 'thread'
6
- require 'examples/simple/word_count_bolt'
7
5
 
8
6
  module RedStorm
9
7
  module Examples
@@ -46,14 +44,14 @@ module RedStorm
46
44
 
47
45
  configure do |env|
48
46
  debug true
49
- set "topology.worker.childopts", "-Djruby.compat.version=RUBY1_9"
47
+ # set "topology.worker.childopts", "-Djruby.compat.version=RUBY1_9"
50
48
  case env
51
49
  when :local
52
50
  max_task_parallelism 3
53
51
  when :cluster
54
52
  max_task_parallelism 5
55
53
  num_workers 20
56
- max_spout_pending(1000);
54
+ max_spout_pending(1000)
57
55
  end
58
56
  end
59
57
  end
@@ -6,7 +6,13 @@ module RedStorm
6
6
  module Examples
7
7
  class VersionSpout < RedStorm::SimpleSpout
8
8
  output_fields :dummy
9
- on_init {log.info("***************** RUBY_VERSION=#{RUBY_VERSION}")}
9
+ on_init do
10
+ log.info("***************** RUBY_VERSION=#{RUBY_VERSION}")
11
+ log.info("***************** JRUBY_VERSION=#{JRUBY_VERSION}")
12
+ log.info("***************** VERSION=#{VERSION}")
13
+ log.info("***************** RUBY_ENGINE=#{RUBY_ENGINE}")
14
+ log.info("***************** RUBY_PLATFORM=#{RUBY_PLATFORM}")
15
+ end
10
16
  on_send {}
11
17
  end
12
18
 
@@ -1,3 +1,4 @@
1
+ require 'red_storm'
1
2
  require 'examples/simple/random_sentence_spout'
2
3
  require 'examples/simple/split_sentence_bolt'
3
4
  require 'examples/simple/word_count_bolt'
@@ -5,25 +6,22 @@ require 'examples/simple/word_count_bolt'
5
6
  module RedStorm
6
7
  module Examples
7
8
  class WordCountTopology < SimpleTopology
8
- spout RandomSentenceSpout, :parallelism => 5
9
+ spout RandomSentenceSpout, :parallelism => 2
9
10
 
10
- bolt SplitSentenceBolt, :parallelism => 8 do
11
+ bolt SplitSentenceBolt, :parallelism => 2 do
11
12
  source RandomSentenceSpout, :shuffle
12
13
  end
13
14
 
14
- bolt WordCountBolt, :parallelism => 12 do
15
+ bolt WordCountBolt, :parallelism => 2 do
15
16
  source SplitSentenceBolt, :fields => ["word"]
16
17
  end
17
18
 
18
19
  configure :word_count do |env|
19
20
  debug true
20
- set "topology.worker.childopts", "-Djruby.compat.version=RUBY1_9"
21
- case env
22
- when :local
23
- max_task_parallelism 3
24
- when :cluster
25
- num_workers 20
26
- max_spout_pending(1000);
21
+ max_task_parallelism 4
22
+ if env == :cluster
23
+ num_workers 6
24
+ max_spout_pending(1000)
27
25
  end
28
26
  end
29
27
 
@@ -1,3 +1,4 @@
1
+ <?xml version="1.0"?>
1
2
  <ivysettings>
2
3
  <settings defaultResolver="repositories"/>
3
4
  <resolvers>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0"?>
2
+ <ivy-module version="2.0">
3
+ <info organisation="redstorm" module="storm-deps"/>
4
+ <dependencies>
5
+ <dependency org="storm" name="storm" rev="0.8.2" conf="default" transitive="true" />
6
+ <override org="org.slf4j" module="slf4j-log4j12" rev="1.6.3"/>
7
+ </dependencies>
8
+ </ivy-module>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0"?>
2
+ <ivy-module version="2.0">
3
+ <info organisation="redstorm" module="topology-deps"/>
4
+ <dependencies>
5
+ <dependency org="org.jruby" name="jruby-core" rev="1.7.3" conf="default" transitive="true"/>
6
+ </dependencies>
7
+ </ivy-module>
@@ -6,3 +6,4 @@ require 'red_storm/configuration'
6
6
  require 'red_storm/simple_bolt'
7
7
  require 'red_storm/simple_spout'
8
8
  require 'red_storm/simple_topology'
9
+ require 'red_storm/simple_drpc_topology'
@@ -7,7 +7,7 @@ TARGET_LIB_DIR = "#{TARGET_DIR}/lib"
7
7
  TARGET_SRC_DIR = "#{TARGET_DIR}/src"
8
8
  TARGET_GEM_DIR = "#{TARGET_DIR}/gems/gems"
9
9
  TARGET_SPECS_DIR = "#{TARGET_DIR}/gems/specifications"
10
- TARGET_CLASSES_DIR = "#{TARGET_DIR}/classes"
10
+ TARGET_CLASSES_DIR = "#{TARGET_DIR}/classes"
11
11
  TARGET_DEPENDENCY_DIR = "#{TARGET_DIR}/dependency"
12
12
  TARGET_DEPENDENCY_UNPACKED_DIR = "#{TARGET_DIR}/dependency-unpacked"
13
13
  TARGET_CLUSTER_JAR = "#{TARGET_DIR}/cluster-topology.jar"
@@ -20,13 +20,15 @@ DST_EXAMPLES = "#{CWD}/examples"
20
20
 
21
21
  SRC_IVY_DIR = "#{RedStorm::REDSTORM_HOME}/ivy"
22
22
  DST_IVY_DIR = "#{CWD}/ivy"
23
- CUSTOM_DEPENDENCIES = "#{CWD}/Dependencies"
24
23
  DEFAULT_IVY_SETTINGS = "#{SRC_IVY_DIR}/settings.xml"
25
24
  CUSTOM_IVY_SETTINGS = "#{DST_IVY_DIR}/settings.xml"
26
-
25
+ DEFAULT_IVY_STORM_DEPENDENCIES = "#{SRC_IVY_DIR}/storm_dependencies.xml"
26
+ CUSTOM_IVY_STORM_DEPENDENCIES = "#{DST_IVY_DIR}/storm_dependencies.xml"
27
+ DEFAULT_IVY_TOPOLOGY_DEPENDENCIES = "#{SRC_IVY_DIR}/topology_dependencies.xml"
28
+ CUSTOM_IVY_TOPOLOGY_DEPENDENCIES = "#{DST_IVY_DIR}/topology_dependencies.xml"
27
29
 
28
30
  module RedStorm
29
-
31
+
30
32
  class Application
31
33
  TASKS_FILE = "#{RedStorm::REDSTORM_HOME}/lib/tasks/red_storm.rake"
32
34
 
@@ -38,7 +40,7 @@ module RedStorm
38
40
  def self.cluster_storm_command(class_file, ruby_mode = nil)
39
41
  "storm jar #{TARGET_CLUSTER_JAR} -Djruby.compat.version=#{RedStorm.jruby_mode_token(ruby_mode)} redstorm.TopologyLauncher cluster #{class_file}"
40
42
  end
41
-
43
+
42
44
  def self.usage
43
45
  puts("usage: redstorm version")
44
46
  puts(" redstorm install")
@@ -76,10 +78,10 @@ module RedStorm
76
78
  end
77
79
 
78
80
  def self.subshell(command)
79
- out = IO.popen(command, {:err => [:child, :out]}) {|io| io.read}
81
+ out = IO.popen(command, STDERR => STDOUT) {|io| io.read}
80
82
  [!!$?.success?, out]
81
83
  end
82
84
 
83
85
  end
84
86
 
85
- end
87
+ end
@@ -20,7 +20,7 @@ module RedStorm
20
20
  private
21
21
 
22
22
  def self.camel_case(s)
23
- s.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
23
+ s.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_|\.)(.)/) { $1.upcase }
24
24
  end
25
25
  end
26
26
  end