rubypitaya 2.7.1 → 2.9.0

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: 9eb7880dd4445994c53e6f44214153fd926df154bc585c200ae007760d542b6b
4
- data.tar.gz: 93d912fb1d953df4ea7f077887c88e832bf52d9515e93583b7dacd0b860486cd
3
+ metadata.gz: 4a6575a4e72c2225e88a02e48a02265797958d1ba5aba36d017bd576fd071c09
4
+ data.tar.gz: b502c84808a5372a6f354f860d5fd673a790851bcff700964b4da24a96c0f50f
5
5
  SHA512:
6
- metadata.gz: 835e30a86c9cdbdc6fe473af6c065b11f8e85cc8cbc9beeaf9a2657b498ab13ac744c9d854ce2421ff13d4339540031763a02ace172785676b53da0e23244566
7
- data.tar.gz: 3b6ac0801420229d445742dc5cb121687c2b60bf960378ea2c741bc4b1cd15ae9eb7f434f886f1b025c568440d4dc879da81b84e55b8f912db857d71236a6775
6
+ metadata.gz: 89b94ffafd69e7216559fcbbd6f55f81a431d4f357771d5ca595ad25a32fcb4e5f0427d5ec21db982ca97f65a19bd3a08b5293ec933e52bd5ac3bcd4c8f26a7e
7
+ data.tar.gz: 6701e279dc7173a1dde84870d2b3728554649a748f36eb2583d0f45b6d5d25432f19de88dc742351f85f8cd81c415fdea414170acbfabd245bcf3a6bbb26c727
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'rubypitaya'
4
4
 
5
- COMMANDS = ['run', 'create']
5
+ COMMANDS = ['run', 'create', 'create-migration']
6
6
 
7
7
  def main
8
8
  if ARGV.size == 0 || !COMMANDS.include?(ARGV[0])
@@ -19,6 +19,10 @@ def main
19
19
  if command == 'create'
20
20
  command_create(ARGV)
21
21
  end
22
+
23
+ if command == 'create-migration'
24
+ command_create_migration(ARGV)
25
+ end
22
26
  end
23
27
 
24
28
  def command_run(argv)
@@ -40,11 +44,25 @@ def command_create(argv)
40
44
  puts "Project #{project_name} created!"
41
45
  end
42
46
 
47
+ def command_create_migration(argv)
48
+ if argv.size <= 1
49
+ show_help_create_migration()
50
+ exit(-1)
51
+ end
52
+
53
+ migration_name = argv[1]
54
+
55
+ migration_file_name = RubyPitaya::RubyPitaya.create_migration(migration_name)
56
+
57
+ puts "Migration #{migration_file_name} created!"
58
+ end
59
+
43
60
  def show_help
44
61
  puts 'Usage: $ rubypitaya [COMMAND]'
45
62
  puts 'COMMAND:'
46
- puts ' run: - Run server'
47
- puts ' create: - Create project'
63
+ puts ' run: - Run server'
64
+ puts ' create: - Create project'
65
+ puts ' create-migration: - Create migration'
48
66
  puts ''
49
67
  end
50
68
 
@@ -53,4 +71,9 @@ def show_help_create
53
71
  puts ''
54
72
  end
55
73
 
74
+ def show_help_create_migration
75
+ puts 'Usage: $ rubypitaya create-migration [migration_name]'
76
+ puts ''
77
+ end
78
+
56
79
  main
@@ -1,3 +1,5 @@
1
+ require 'erb'
2
+ require 'ostruct'
1
3
  require 'fileutils'
2
4
 
3
5
  require 'rubypitaya/core/main'
@@ -16,5 +18,24 @@ module RubyPitaya
16
18
 
17
19
  FileUtils.cp_r app_template_path, project_path
18
20
  end
21
+
22
+ def self.create_migration(migration_name)
23
+ migration_name = "#{migration_name}_migration" unless migration_name.underscore.end_with?('migration')
24
+ migration_timestamp = Time.now.utc.to_i
25
+ migration_file_name = "#{migration_timestamp}_#{migration_name.underscore}.rb"
26
+ migration_class_name = migration_name.camelcase
27
+
28
+ template_struct = OpenStruct.new(
29
+ class_name: migration_class_name,
30
+ )
31
+
32
+ template = File.open(Path::MIGRATION_TEMPLATE_PATH, &:read)
33
+ template_result = ERB.new(template).result(template_struct.instance_eval { binding })
34
+
35
+ migration_file_path = File.join(Path::MIGRATIONS_FOLDER_PATH, migration_file_name)
36
+ File.open(migration_file_path, 'w') { |f| f.write(template_result) }
37
+
38
+ migration_file_name
39
+ end
19
40
  end
20
41
  end
@@ -1,6 +1,6 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'rubypitaya', '2.7.1'
3
+ gem 'rubypitaya', '2.9.0'
4
4
 
5
5
  group :development do
6
6
  gem 'pry', '0.12.2'
@@ -69,7 +69,7 @@ GEM
69
69
  rspec-support (~> 3.8.0)
70
70
  rspec-support (3.8.3)
71
71
  ruby2_keywords (0.0.2)
72
- rubypitaya (2.7.1)
72
+ rubypitaya (2.9.0)
73
73
  activerecord (= 6.0.2)
74
74
  etcdv3 (= 0.10.2)
75
75
  eventmachine (= 1.2.7)
@@ -106,7 +106,7 @@ DEPENDENCIES
106
106
  listen (= 3.2.1)
107
107
  pry (= 0.12.2)
108
108
  rspec (= 3.8.0)
109
- rubypitaya (= 2.7.1)
109
+ rubypitaya (= 2.9.0)
110
110
 
111
111
  BUNDLED WITH
112
112
  1.17.2
@@ -1,3 +1,9 @@
1
+ IMAGE_TAG ?= latest
2
+ IMAGE_REGISTRY ?= [put-your-registry-here]
3
+ KUBE_NAMESPACE ?= [put-your-namespace-here]
4
+ KUBE_DEPLOYMENT_SERVER ?= rubypitaya
5
+ KUBECONTEXT ?= ''
6
+
1
7
  ## Run ruby pitaya metagame project
2
8
  run:
3
9
  @docker-compose run --service-ports --rm rubypitaya bundle exec rubypitaya run
@@ -22,6 +28,10 @@ console:
22
28
  bash:
23
29
  @docker-compose run --service-ports --rm rubypitaya bash
24
30
 
31
+ ## Create new migrgation. NAME=[migration-name]
32
+ create-migration:
33
+ @docker-compose run --service-ports --rm rubypitaya-console bundle exec rubypitaya create-migration $(NAME)
34
+
25
35
  ## Create database
26
36
  db-create:
27
37
  @docker-compose run --service-ports --rm rubypitaya bundle exec rake db:create
@@ -30,6 +40,10 @@ db-create:
30
40
  db-migrate:
31
41
  @docker-compose run --service-ports --rm rubypitaya bundle exec rake db:migrate
32
42
 
43
+ ## Show migrations status on database
44
+ db-migrate-status:
45
+ @docker-compose run --service-ports --rm rubypitaya bundle exec rake db:status
46
+
33
47
  ## Rollback migrations STEP=1
34
48
  db-rollback:
35
49
  @docker-compose run --service-ports --rm -e STEP="$(STEP)" rubypitaya bundle exec rake db:rollback
@@ -48,15 +62,15 @@ generate-gemfilelock:
48
62
 
49
63
  ## Build image to production environment
50
64
  prod-build-image:
51
- @docker build . -f docker/prod/Dockerfile -t [registry-address]:$(IMAGE_TAG)
65
+ @docker build . -f docker/prod/Dockerfile -t $(IMAGE_REGISTRY):$(IMAGE_TAG)
52
66
 
53
67
  ## Push prod image to gitlab
54
68
  prod-push-image:
55
- @docker push [registry-address]:$(IMAGE_TAG)
69
+ @docker push $(IMAGE_REGISTRY):$(IMAGE_TAG)
56
70
 
57
71
  ## Deploy prod image to kubernetes cluster
58
72
  prod-deploy-image:
59
- kubectl -n $(PROD_NAMESPACE) set image deployment rubypitaya rubypitaya=[registry-address]:$(IMAGE_TAG)
73
+ kubectl --context='$(KUBECONTEXT)' -n $(KUBE_NAMESPACE) set image deployment $(KUBE_DEPLOYMENT_SERVER) $(KUBE_DEPLOYMENT_SERVER)=$(IMAGE_REGISTRY):$(IMAGE_TAG)
60
74
 
61
75
  .DEFAULT_GOAL := show-help
62
76
 
@@ -98,6 +112,3 @@ show-help:
98
112
  } \
99
113
  printf "\n"; \
100
114
  }'
101
-
102
-
103
-
@@ -75,6 +75,25 @@ namespace :db do
75
75
  puts 'Database deleted.'
76
76
  end
77
77
 
78
+ desc 'migration status'
79
+ task :status do
80
+ environment_name = ENV.fetch("RUBYPITAYA_ENV") { 'development' }
81
+ database_config = RubyPitaya::DatabaseConfig.new(environment_name, RubyPitaya::Path::DATABASE_CONFIG_PATH)
82
+ connection_data = database_config.connection_data
83
+ migrations_paths = [RubyPitaya::Path::Core::MIGRATIONS_FOLDER_PATH]
84
+ migrations_paths += RubyPitaya::Path::Plugins::MIGRATIONS_FOLDER_PATHS
85
+ migrations_paths += [RubyPitaya::Path::MIGRATIONS_FOLDER_PATH]
86
+
87
+ ActiveRecord::Base.establish_connection(connection_data)
88
+ ActiveRecord::Migrator.migrations_paths = migrations_paths
89
+ ActiveRecord::Base.connection.migration_context.migrations_status.each do |status, version, name|
90
+ puts "#{status.center(8)} #{version.ljust(14)} #{name}"
91
+ end
92
+ ActiveRecord::Base.connection.close
93
+
94
+ puts 'Rollback done.'
95
+ end
96
+
78
97
  desc 'Reset the database'
79
98
  task :reset do
80
99
  Rake::Task['db:drop'].invoke
@@ -27,6 +27,12 @@ module MyApp
27
27
  # - methods:
28
28
  # - [](key)
29
29
  # - get config file by config path
30
+ # - log
31
+ # - class: Logger
32
+ # - link: https://ruby-doc.org/stdlib-2.6.4/libdoc/logger/rdoc/Logger.html
33
+ # - methods:
34
+ # - info
35
+ # - log information
30
36
 
31
37
  def run(initializer_content)
32
38
  bll = initializer_content.bll
@@ -35,5 +41,9 @@ module MyApp
35
41
 
36
42
  bll.add_instance(:player, playerBll)
37
43
  end
44
+
45
+ def self.path
46
+ __FILE__
47
+ end
38
48
  end
39
49
  end
@@ -1,3 +1,5 @@
1
+ require 'rubypitaya/core/app/models/user'
2
+
1
3
  User.class_eval do
2
4
  # has_one :player
3
5
  end
@@ -6,25 +6,35 @@ require 'active_record'
6
6
  require 'rubypitaya'
7
7
  require 'rubypitaya/core/database_config'
8
8
 
9
+ # Database connection
9
10
  environment_name = ENV.fetch("RUBYPITAYA_ENV") { 'development' }
10
11
  database_config = RubyPitaya::DatabaseConfig.new(environment_name, RubyPitaya::Path::DATABASE_CONFIG_PATH)
11
12
  ActiveRecord::Base.establish_connection(database_config.connection_data)
12
13
  ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
13
14
  ActiveSupport::LogSubscriber.colorize_logging = true
14
15
 
16
+ # Loading core files
15
17
  Gem.find_files('rubypitaya/**/*.rb').each do |path|
16
18
  require path unless path.end_with?('spec.rb') ||
17
19
  path.include?('db/migration') ||
20
+ path.include?('core/templates') ||
18
21
  path.include?('app-template')
19
22
  end
20
23
 
21
- app_files_path = File.join(RubyPitaya::Path::APP_FOLDER_PATH, '**/*.rb')
22
- Dir[app_files_path].each do |path|
23
- require path unless path.end_with?('spec.rb') ||
24
- path.include?('db/migration')
24
+ # Loading application files
25
+ app_folder_paths = RubyPitaya::Path::Plugins::APP_FOLDER_PATHS + [RubyPitaya::Path::APP_FOLDER_PATH]
26
+ app_folder_paths.each do |app_folder_path|
27
+ app_files_path = File.join(app_folder_path, '**/*.rb')
28
+
29
+ Dir[app_files_path].each do |path|
30
+ require path unless path.end_with?('spec.rb') ||
31
+ path.include?('db/migration')
32
+ end
25
33
  end
26
34
 
35
+ # Starting irb
27
36
  require 'irb'
28
37
  IRB.start(__FILE__)
29
38
 
39
+ # Closing database connection
30
40
  ActiveRecord::Base.connection.close
@@ -56,6 +56,7 @@ services:
56
56
  working_dir: '/app/rubypitaya'
57
57
  volumes:
58
58
  - '.:/app/rubypitaya'
59
+ - '/var/run/docker.sock:/var/run/docker.sock'
59
60
  ports:
60
61
  - '80:4567'
61
62
  environment:
@@ -1,7 +1,7 @@
1
1
  FROM ruby:2.6.6
2
2
 
3
3
  RUN apt update
4
- RUN apt install -y git vim postgresql-client --no-install-recommends
4
+ RUN apt install -y git vim postgresql-client docker.io --no-install-recommends
5
5
 
6
6
  WORKDIR /app/rubypitaya/
7
7
 
@@ -16,7 +16,7 @@ spec:
16
16
  spec:
17
17
  containers:
18
18
  - name: rubypitaya
19
- image: git.topfreegames.com:4567/prestes/tech-prototype/rubypitaya:latest
19
+ image: registry.gitlab.com/lucianopc/ruby-pitaya:latest
20
20
  command: ["bundle", "exec", "rubypitaya", "run"]
21
21
  ports:
22
22
  - containerPort: 4567
@@ -45,4 +45,4 @@ spec:
45
45
  - name: DATABASE_NAME
46
46
  value: "ruby_pitaya"
47
47
  imagePullSecrets:
48
- - name: gitlab-registry
48
+ - name: gitlab-registry
@@ -2,7 +2,7 @@ kind: Role
2
2
  apiVersion: apps/v1
3
3
  metadata:
4
4
  namespace: default
5
- name: namespace
5
+ name: rubypitaya
6
6
  rules:
7
7
  - apiGroups:
8
8
  - ""
@@ -4,10 +4,11 @@ module RubyPitaya
4
4
 
5
5
  class_attribute :non_authenticated_routes, default: []
6
6
 
7
- attr_accessor :bll, :redis, :setup, :config, :params, :session, :postman
7
+ attr_accessor :bll, :log, :redis, :setup, :config, :params, :session, :postman
8
8
 
9
9
  def initialize
10
10
  @bll = nil
11
+ @log = nil
11
12
  @redis = nil
12
13
  @setup = nil
13
14
  @config = nil
@@ -53,7 +53,7 @@ module RubyPitaya
53
53
  end
54
54
 
55
55
  def call(handler_name, action_name, session, postman, redis, setup, config,
56
- bll, params)
56
+ bll, log, params)
57
57
  unless @handler_name_map.include?(handler_name)
58
58
  return {
59
59
  code: StatusCodes::CODE_HANDLER_NOT_FOUND,
@@ -71,6 +71,7 @@ module RubyPitaya
71
71
  handler = @handler_name_map[handler_name]
72
72
 
73
73
  handler.bll = bll
74
+ handler.log = log
74
75
  handler.redis = redis
75
76
  handler.setup = setup
76
77
  handler.config = config
@@ -2,6 +2,10 @@ module RubyPitaya
2
2
 
3
3
  class InitializerBase
4
4
 
5
+ def self.path
6
+ __FILE__
7
+ end
8
+
5
9
  def run(main)
6
10
  end
7
11
  end
@@ -6,7 +6,25 @@ module RubyPitaya
6
6
  class InitializerBroadcast
7
7
 
8
8
  def run(initializer_content)
9
+ app_classes = []
10
+ plugin_classes = []
11
+
9
12
  ObjectSpace.each_object(InitializerBase.singleton_class) do |klass|
13
+ is_plugin_class = klass.path.include?('plugins')
14
+
15
+ if is_plugin_class
16
+ plugin_classes << klass
17
+ else
18
+ app_classes << klass
19
+ end
20
+ end
21
+
22
+ plugin_classes.each do |klass|
23
+ instance = klass.new
24
+ instance.run(initializer_content)
25
+ end
26
+
27
+ app_classes.each do |klass|
10
28
  instance = klass.new
11
29
  instance.run(initializer_content)
12
30
  end
@@ -2,10 +2,11 @@ module RubyPitaya
2
2
 
3
3
  class InitializerContent
4
4
 
5
- attr_reader :bll, :redis, :config
5
+ attr_reader :bll, :log, :redis, :setup, :config
6
6
 
7
- def initialize(bll, redis, setup, config)
7
+ def initialize(bll, log, redis, setup, config)
8
8
  @bll = bll
9
+ @log = log
9
10
  @redis = redis
10
11
  @setup = setup
11
12
  @config = config
@@ -1,4 +1,5 @@
1
1
  require 'socket'
2
+ require 'logger'
2
3
  require 'securerandom'
3
4
  require 'active_model'
4
5
 
@@ -8,6 +9,7 @@ require 'rubypitaya/core/config'
8
9
  require 'rubypitaya/core/session'
9
10
  require 'rubypitaya/core/postman'
10
11
  require 'rubypitaya/core/parameters'
12
+ require 'rubypitaya/core/http_routes'
11
13
  require 'rubypitaya/core/routes_base'
12
14
  require 'rubypitaya/core/status_codes'
13
15
  require 'rubypitaya/core/handler_router'
@@ -31,6 +33,12 @@ module RubyPitaya
31
33
  @environment_name = ENV.fetch("RUBYPITAYA_ENV") { 'development' }
32
34
  @is_development_environment = @environment_name == 'development'
33
35
 
36
+ @log = Logger.new('/proc/self/fd/1')
37
+ @log.level = Logger::INFO
38
+ @log.formatter = proc do |severity, datetime, progname, msg|
39
+ "#{msg}\n"
40
+ end
41
+
34
42
  @application_files_importer = ApplicationFilesImporter.new
35
43
  @application_files_importer.import
36
44
  @application_files_importer.auto_reload if @is_development_environment
@@ -71,6 +79,7 @@ module RubyPitaya
71
79
  @bll = InstanceHolder.new
72
80
 
73
81
  @initializer_content = InitializerContent.new(@bll,
82
+ @log,
74
83
  @redis_connector.redis,
75
84
  @setup,
76
85
  @config)
@@ -87,6 +96,7 @@ module RubyPitaya
87
96
 
88
97
  def run_http
89
98
  HttpRoutes.set :bll, @bll
99
+ HttpRoutes.set :log, @log
90
100
  HttpRoutes.set :setup, @setup
91
101
  HttpRoutes.set :config, @config
92
102
  HttpRoutes.set :views, [Path::HTTP_VIEWS_PATH] + Path::Plugins::HTTP_VIEWS_PATHS
@@ -106,7 +116,7 @@ module RubyPitaya
106
116
  Signal.trap("SIGQUIT") { throw :sig_shutdown }
107
117
  Signal.trap("SIGTERM") { throw :sig_shutdown }
108
118
 
109
- puts "Server started!"
119
+ @log.info "Server started!"
110
120
  run_nats_connection
111
121
  end
112
122
 
@@ -121,7 +131,7 @@ module RubyPitaya
121
131
  return if @is_shutting_down
122
132
  @is_shutting_down = true
123
133
 
124
- puts "Server shutting down..."
134
+ @log.info "Server shutting down..."
125
135
 
126
136
  @etcd_connector.disconnect
127
137
  @database_connector.disconnect
@@ -152,22 +162,22 @@ module RubyPitaya
152
162
 
153
163
  handler_name, action_name = message_route.split('.')[1..-1]
154
164
 
155
- puts "request -> route: #{message_route}"
156
- puts " -> data: #{message_data}"
165
+ @log.info "request -> route: #{message_route}"
166
+ @log.info " -> data: #{message_data}"
157
167
 
158
168
  response = @handler_router.call(handler_name, action_name, @session,
159
169
  @postman, @redis_connector.redis,
160
- @setup, @config, @bll, params)
170
+ @setup, @config, @bll, @log, params)
161
171
 
162
172
  delta_time_seconds = Time.now.to_i - start_time_seconds
163
173
 
164
- puts "response [#{delta_time_seconds}s] -> #{response.to_json}"
174
+ @log.info "response [#{delta_time_seconds}s] -> #{response.to_json}"
165
175
 
166
176
  response
167
177
  end
168
178
  rescue Exception => error
169
- puts "ERROR: #{error}"
170
- puts error.backtrace
179
+ @log.info "ERROR: #{error}"
180
+ @log.info error.backtrace
171
181
  run_nats_connection
172
182
  end
173
183
  end
@@ -2,6 +2,7 @@ module RubyPitaya
2
2
 
3
3
  class Path
4
4
  APP_TEMPLATE_FOLDER_PATH = File.join(__dir__, '../app-template/')
5
+ MIGRATION_TEMPLATE_PATH = File.join(__dir__, 'templates/template_migration.rb.erb')
5
6
 
6
7
  DATABASE_CONFIG_PATH = File.join(Dir.pwd, 'config/database.yml')
7
8
 
@@ -15,6 +16,7 @@ module RubyPitaya
15
16
 
16
17
  HTTP_VIEWS_PATH = File.join(Dir.pwd, 'app/http/views')
17
18
 
19
+
18
20
  class Core
19
21
  APP_FOLDER_PATH = File.join(__dir__, 'app/')
20
22
  MIGRATIONS_FOLDER_PATH = File.join(__dir__, 'db/migration/')
@@ -0,0 +1,13 @@
1
+ require 'active_record'
2
+
3
+ class <%= class_name %> < ActiveRecord::Migration[5.1]
4
+
5
+ enable_extension 'pgcrypto'
6
+
7
+ def change
8
+ create_table :[table-name-here-in-plural], id: :uuid do |t|
9
+ # t.belongs_to :user, type: :uuid
10
+ t.timestamps null: false
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module RubyPitaya
2
- VERSION = '2.7.1'
2
+ VERSION = '2.9.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubypitaya
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.1
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luciano Prestes Cavalcanti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-30 00:00:00.000000000 Z
11
+ date: 2020-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
96
  version: 2.1.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: ostruct
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 0.1.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 0.1.0
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: sinatra-contrib
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -285,6 +299,7 @@ files:
285
299
  - "./lib/rubypitaya/core/session.rb"
286
300
  - "./lib/rubypitaya/core/setup.rb"
287
301
  - "./lib/rubypitaya/core/status_codes.rb"
302
+ - "./lib/rubypitaya/core/templates/template_migration.rb.erb"
288
303
  - "./lib/rubypitaya/version.rb"
289
304
  - bin/rubypitaya
290
305
  homepage: https://gitlab.com/LucianoPC/ruby-pitaya