redstorm 0.6.4 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
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