leafy-ruby 0.0.2 → 0.1.1
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 +5 -5
- data/.gitignore +3 -1
- data/.travis.yml +21 -5
- data/README.md +31 -10
- data/leafy.gemspec +9 -8
- data/lib/leafy.rb +12 -0
- data/lib/leafy/coder/default.rb +5 -0
- data/lib/leafy/coder/mock.rb +13 -0
- data/lib/leafy/configuration.rb +19 -0
- data/lib/leafy/field_value_collection.rb +27 -18
- data/lib/leafy/mixin/active_record/fields.rb +4 -2
- data/lib/leafy/mixin/poro/fields.rb +4 -2
- data/lib/leafy/version.rb +2 -2
- metadata +21 -30
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: c26d92812d5952076dba88b883f46fea4213c681
|
|
4
|
+
data.tar.gz: e6d9d2473e0c31947cdaef28940464aad93f2897
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b16830890b1ae949d5948518e14c89b2a8cf84d8f6e238c772c3bb604f0add0f5bb635766c59b97b496c3e0c43203dd05db9e73aec33f6d9e3e30f84290e0202
|
|
7
|
+
data.tar.gz: b595b079f9196968451ea11688f68ca1ba74e8ea5f2d07b468aaf24f8e937736c0ef6300d05ad8afd398587a307feae2fc5c90c9fceab0ec84909f2a2127d1e7
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -2,21 +2,35 @@ sudo: false
|
|
|
2
2
|
|
|
3
3
|
language: ruby
|
|
4
4
|
|
|
5
|
+
services:
|
|
6
|
+
- postgresql
|
|
7
|
+
|
|
8
|
+
addons:
|
|
9
|
+
postgresql: "10"
|
|
10
|
+
apt:
|
|
11
|
+
packages:
|
|
12
|
+
- postgresql-10
|
|
13
|
+
- postgresql-client-10
|
|
14
|
+
|
|
5
15
|
rvm:
|
|
6
|
-
- 2.2
|
|
7
16
|
- 2.3
|
|
8
17
|
- 2.4
|
|
9
18
|
- 2.5
|
|
19
|
+
- 2.6
|
|
20
|
+
- 2.7
|
|
10
21
|
- ruby-head
|
|
11
|
-
- jruby-9.2.0.0
|
|
12
22
|
- jruby-head
|
|
13
23
|
|
|
14
24
|
matrix:
|
|
15
25
|
allow_failures:
|
|
26
|
+
- rvm: jruby-9.2.0.0
|
|
27
|
+
- rvm: 2.2
|
|
16
28
|
- rvm: ruby-head
|
|
17
29
|
- rvm: jruby-head
|
|
18
30
|
|
|
19
|
-
before_install:
|
|
31
|
+
before_install:
|
|
32
|
+
- yes | gem update --system --force
|
|
33
|
+
- gem install bundler
|
|
20
34
|
|
|
21
35
|
before_script:
|
|
22
36
|
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
|
@@ -32,7 +46,9 @@ cache: bundler
|
|
|
32
46
|
|
|
33
47
|
env:
|
|
34
48
|
global:
|
|
35
|
-
CC_TEST_REPORTER_ID=5c94ea74238649cee4b51abbe647de62a3d63971eaeca9aa58c82b62d9a0e6a8
|
|
36
|
-
COVERAGE=1
|
|
49
|
+
- CC_TEST_REPORTER_ID=5c94ea74238649cee4b51abbe647de62a3d63971eaeca9aa58c82b62d9a0e6a8
|
|
50
|
+
- COVERAGE=1
|
|
51
|
+
- PGPORT=5433
|
|
52
|
+
|
|
37
53
|
|
|
38
54
|
|
data/README.md
CHANGED
|
@@ -30,13 +30,13 @@ Include "Plain old ruby object" mixin into your class definition to start using
|
|
|
30
30
|
```ruby
|
|
31
31
|
class SchemaHost < ActiveRecord::Base
|
|
32
32
|
include Leafy::Mixin::Schema[:poro]
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
attr_accessor :leafy_data
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
class FieldsHost < ActiveRecord::Base
|
|
38
38
|
include Leafy::Mixin::Fields[:poro]
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
attr_accessor :leafy_data
|
|
41
41
|
attr_accessor :leafy_fields
|
|
42
42
|
end
|
|
@@ -46,7 +46,7 @@ Schema mixin introduces next methods:
|
|
|
46
46
|
|
|
47
47
|
- `#leafy_fields (Schema)` returns Schema instance allowing you to iterate through custom attribute definitions.
|
|
48
48
|
- `#leafy_fields=` schema setter method
|
|
49
|
-
- `#leafy_fields_attributes=` nested attributes setter method
|
|
49
|
+
- `#leafy_fields_attributes=` nested attributes setter method
|
|
50
50
|
|
|
51
51
|
Fields mixin:
|
|
52
52
|
|
|
@@ -56,7 +56,7 @@ Fields mixin:
|
|
|
56
56
|
|
|
57
57
|
**Please note**:
|
|
58
58
|
Leafy is stateless and changing Schema instance won't reflect on your active record model instance.
|
|
59
|
-
For changes to take place you have to explicitly assign schema or attributes data to the model.
|
|
59
|
+
For changes to take place you have to explicitly assign schema or attributes data to the model.
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
|
|
@@ -102,7 +102,7 @@ Add migration
|
|
|
102
102
|
add_column :schema_hosts, :leafy_data, :text, null: false, default: "{}"
|
|
103
103
|
add_column :fields_hosts, :leafy_data, :text, null: false, default: "{}"
|
|
104
104
|
# for postgresql
|
|
105
|
-
# add_column :leafy_data, :jsonb, null: false, default: {}
|
|
105
|
+
# add_column :leafy_data, :jsonb, null: false, default: {}
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
Update your models
|
|
@@ -114,9 +114,9 @@ end
|
|
|
114
114
|
|
|
115
115
|
class FieldsHost < ActiveRecord::Base
|
|
116
116
|
include Leafy::Mixin::Fields[:active_record]
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
belongs_to :schema_host, required: true
|
|
119
|
-
delegate :leafy_fields, to: :schema_host
|
|
119
|
+
delegate :leafy_fields, to: :schema_host
|
|
120
120
|
end
|
|
121
121
|
```
|
|
122
122
|
|
|
@@ -143,6 +143,27 @@ target.leafy_values
|
|
|
143
143
|
# => { "id_1": 123, "id_2": "test", "id_3": Time.new(2018,10,10, 10,10,10, "+03:00") }
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
+
## Configuration
|
|
147
|
+
|
|
148
|
+
In you initialization code
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
class MyLovelyCoder
|
|
152
|
+
def dump(data)
|
|
153
|
+
"lovely_#{data}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def load(data)
|
|
157
|
+
data.split("_")[1]
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
Leafy.configure do |config|
|
|
162
|
+
# you may wonna use oj instead
|
|
163
|
+
config.coder = MyLovelyCoder.new
|
|
164
|
+
end
|
|
165
|
+
```
|
|
166
|
+
|
|
146
167
|
## Adding your own types
|
|
147
168
|
|
|
148
169
|
Leafy allows adding your own data types
|
|
@@ -155,8 +176,8 @@ class MyComplexTypeConverter
|
|
|
155
176
|
def self.load(json_string)
|
|
156
177
|
# parsing logic
|
|
157
178
|
return MyComplexType.new(parsed_data)
|
|
158
|
-
end
|
|
159
|
-
|
|
179
|
+
end
|
|
180
|
+
|
|
160
181
|
def self.dump(my_complex_type_instance)
|
|
161
182
|
# serializing logic
|
|
162
183
|
return json
|
|
@@ -166,7 +187,7 @@ end
|
|
|
166
187
|
Leafy.register_converter(:complex_type, MyComplexTypeConverter)
|
|
167
188
|
```
|
|
168
189
|
|
|
169
|
-
|
|
190
|
+
|
|
170
191
|
|
|
171
192
|
## Contributing
|
|
172
193
|
|
data/leafy.gemspec
CHANGED
|
@@ -23,25 +23,26 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
24
24
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
25
25
|
end
|
|
26
|
-
spec.bindir = "
|
|
27
|
-
spec.executables = spec.files.grep(%r{^
|
|
26
|
+
spec.bindir = "bin"
|
|
27
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
28
28
|
spec.require_paths = ["lib"]
|
|
29
29
|
spec.required_ruby_version = '>= 2.2'
|
|
30
|
+
spec.platform = Gem::Platform::RUBY
|
|
30
31
|
|
|
31
|
-
spec.add_development_dependency "bundler", "~> 1.16"
|
|
32
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
|
33
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
34
|
-
spec.add_development_dependency "simplecov"
|
|
33
|
+
spec.add_development_dependency "simplecov", '~> 0.17.1'
|
|
35
34
|
|
|
36
|
-
if RUBY_VERSION >= "2.
|
|
37
|
-
spec.add_development_dependency "activerecord", "~>
|
|
35
|
+
if RUBY_VERSION >= "2.5.0"
|
|
36
|
+
spec.add_development_dependency "activerecord", "~> 6.0"
|
|
38
37
|
else
|
|
39
|
-
spec.add_development_dependency "activerecord", "~>
|
|
38
|
+
spec.add_development_dependency "activerecord", "~> 5.2"
|
|
40
39
|
end
|
|
41
40
|
|
|
42
41
|
if RUBY_ENGINE == "jruby"
|
|
43
42
|
spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", "51"
|
|
43
|
+
spec.add_development_dependency "pg_jruby"
|
|
44
44
|
else
|
|
45
45
|
spec.add_development_dependency "sqlite3"
|
|
46
|
+
spec.add_development_dependency "pg"
|
|
46
47
|
end
|
|
47
48
|
end
|
data/lib/leafy.rb
CHANGED
|
@@ -9,10 +9,22 @@ require "leafy/field_value_collection"
|
|
|
9
9
|
Dir[File.expand_path("../leafy/converter/**/*.rb", __FILE__)].each { |f| require f }
|
|
10
10
|
require "leafy/mixin/schema"
|
|
11
11
|
require "leafy/mixin/fields"
|
|
12
|
+
require "leafy/coder/default"
|
|
13
|
+
require "leafy/coder/mock"
|
|
14
|
+
require "leafy/configuration"
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
# module definition
|
|
15
18
|
module Leafy
|
|
19
|
+
|
|
20
|
+
def self.configure
|
|
21
|
+
yield configuration if block_given?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.configuration
|
|
25
|
+
@config ||= Leafy::Configuration.new
|
|
26
|
+
end
|
|
27
|
+
|
|
16
28
|
def self.register_converter(name, converter)
|
|
17
29
|
raise(ArgumentError, "converter is not provided") if converter.nil?
|
|
18
30
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module Leafy
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :coder
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@coder = Leafy::Coder::Default
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def coder=(value)
|
|
12
|
+
if value.respond_to?(:dump) && value.respond_to?(:load)
|
|
13
|
+
@coder = value
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
raise ArgumentError, "coder must implement #dump and #load"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -4,6 +4,23 @@ module Leafy
|
|
|
4
4
|
class FieldValueCollection
|
|
5
5
|
include ::Enumerable
|
|
6
6
|
|
|
7
|
+
def initialize(leafy_fields, field_values: {}, ar_json: false)
|
|
8
|
+
@leafy_fields = leafy_fields
|
|
9
|
+
@coder = ar_json ? Leafy::Coder::Mock.new : Leafy.configuration.coder
|
|
10
|
+
self.leafy_field_values = field_values
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def leafy_field_values=(data)
|
|
14
|
+
@leafy_field_values = @leafy_fields.map do |custom_field|
|
|
15
|
+
Leafy::FieldValue.new(
|
|
16
|
+
id: custom_field.id,
|
|
17
|
+
name: custom_field.name,
|
|
18
|
+
raw: data[custom_field.id],
|
|
19
|
+
type: custom_field.type
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
7
24
|
def each
|
|
8
25
|
if block_given?
|
|
9
26
|
@leafy_field_values.each { |i| yield i }
|
|
@@ -20,18 +37,6 @@ module Leafy
|
|
|
20
37
|
count
|
|
21
38
|
end
|
|
22
39
|
|
|
23
|
-
def initialize(leafy_fields, values = {})
|
|
24
|
-
@leafy_fields = leafy_fields
|
|
25
|
-
@leafy_field_values = leafy_fields.map do |custom_field|
|
|
26
|
-
Leafy::FieldValue.new(
|
|
27
|
-
id: custom_field.id,
|
|
28
|
-
name: custom_field.name,
|
|
29
|
-
raw: values[custom_field.id],
|
|
30
|
-
type: custom_field.type
|
|
31
|
-
)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
40
|
def values
|
|
36
41
|
inject({}) do |acc, field_value|
|
|
37
42
|
acc[field_value.id] = field_value.value
|
|
@@ -40,19 +45,23 @@ module Leafy
|
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
def values=(attributes = {})
|
|
43
|
-
|
|
48
|
+
_attributes = {}
|
|
49
|
+
|
|
50
|
+
attributes.each { |key, value| _attributes[key.to_s] = value }
|
|
44
51
|
|
|
45
52
|
@leafy_field_values.each do |field_value|
|
|
46
|
-
field_value.value =
|
|
53
|
+
field_value.value = _attributes[field_value.id]
|
|
47
54
|
end
|
|
48
55
|
end
|
|
49
56
|
|
|
50
|
-
def
|
|
51
|
-
|
|
57
|
+
def dump
|
|
58
|
+
data = {}
|
|
59
|
+
each { |field_value| data[field_value.id] = field_value.raw }
|
|
60
|
+
@coder.dump(data)
|
|
52
61
|
end
|
|
53
62
|
|
|
54
|
-
def
|
|
55
|
-
|
|
63
|
+
def load(data)
|
|
64
|
+
self.leafy_field_values = @coder.load(data)
|
|
56
65
|
end
|
|
57
66
|
|
|
58
67
|
end
|
|
@@ -24,11 +24,13 @@ module Leafy
|
|
|
24
24
|
field_value_list = leafy_field_values
|
|
25
25
|
field_value_list.values = attributes
|
|
26
26
|
|
|
27
|
-
self.leafy_data =
|
|
27
|
+
self.leafy_data = field_value_list.dump
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def leafy_field_values
|
|
31
|
-
::Leafy::FieldValueCollection.
|
|
31
|
+
field_value_collection = ::Leafy::FieldValueCollection.new(leafy_fields, ar_json: activerecord_json_column?)
|
|
32
|
+
field_value_collection.load(leafy_data || '{}')
|
|
33
|
+
field_value_collection
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
end
|
|
@@ -22,11 +22,13 @@ module Leafy
|
|
|
22
22
|
field_value_list = leafy_field_values
|
|
23
23
|
field_value_list.values = attributes
|
|
24
24
|
|
|
25
|
-
self._leafy_data =
|
|
25
|
+
self._leafy_data = field_value_list.dump
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def leafy_field_values
|
|
29
|
-
::Leafy::FieldValueCollection.
|
|
29
|
+
field_value_collection = ::Leafy::FieldValueCollection.new(leafy_fields)
|
|
30
|
+
field_value_collection.load(_leafy_data || '{}')
|
|
31
|
+
field_value_collection
|
|
30
32
|
end
|
|
31
33
|
end
|
|
32
34
|
end
|
data/lib/leafy/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: leafy-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evgeny Stepanov
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-02-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: rspec
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '3.0'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '3.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: simplecov
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 0.17.1
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
40
|
+
version: 0.17.1
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
42
|
+
name: activerecord
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
47
|
+
version: '5.2'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
54
|
+
version: '5.2'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
56
|
+
name: sqlite3
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - ">="
|
|
@@ -67,21 +67,7 @@ dependencies:
|
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "~>"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '5.0'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "~>"
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '5.0'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: sqlite3
|
|
70
|
+
name: pg
|
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
|
86
72
|
requirements:
|
|
87
73
|
- - ">="
|
|
@@ -101,7 +87,9 @@ description: |2
|
|
|
101
87
|
It ships with several basic data types and allows you to add your own data type converters.
|
|
102
88
|
email:
|
|
103
89
|
- estepnv@icloud.com
|
|
104
|
-
executables:
|
|
90
|
+
executables:
|
|
91
|
+
- console
|
|
92
|
+
- setup
|
|
105
93
|
extensions: []
|
|
106
94
|
extra_rdoc_files: []
|
|
107
95
|
files:
|
|
@@ -116,6 +104,9 @@ files:
|
|
|
116
104
|
- bin/setup
|
|
117
105
|
- leafy.gemspec
|
|
118
106
|
- lib/leafy.rb
|
|
107
|
+
- lib/leafy/coder/default.rb
|
|
108
|
+
- lib/leafy/coder/mock.rb
|
|
109
|
+
- lib/leafy/configuration.rb
|
|
119
110
|
- lib/leafy/converter/bool_converter.rb
|
|
120
111
|
- lib/leafy/converter/date_converter.rb
|
|
121
112
|
- lib/leafy/converter/datetime_converter.rb
|
|
@@ -157,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
157
148
|
version: '0'
|
|
158
149
|
requirements: []
|
|
159
150
|
rubyforge_project:
|
|
160
|
-
rubygems_version: 2.
|
|
151
|
+
rubygems_version: 2.5.2.3
|
|
161
152
|
signing_key:
|
|
162
153
|
specification_version: 4
|
|
163
154
|
summary: Toolkit for custom attributes in Ruby apps
|