tulirb 0.1.1 → 1.0.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 +4 -4
- data/.rubocop.yml +2 -1
- data/CHANGELOG.md +12 -4
- data/Dockerfile +10 -0
- data/README.md +5 -1
- data/Rakefile +59 -4
- data/docker-compose.yml +8 -0
- data/ext/tulirb/extconf.rb +6 -2
- data/ext/tulirb/tulirb.c +27 -19
- data/lib/tulirb/version.rb +1 -1
- data/tulirb.gemspec +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 439ab7a08e2ecbe74674156913a01255c008e7e4768821dae21f4ccef2db95a5
|
4
|
+
data.tar.gz: 20df5ea27a9ef2c6a16aae50688b2fab30b3f4c6327ef0213b188642483b1249
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '059b4963c53683830ac7c169596ee001e11bab1c8ebf4ec6cd95d95d92bfdc22c7b5f5647b4698d9b2528c381d5089c52ede0084a97e53d7370508416c620284'
|
7
|
+
data.tar.gz: 0edb7138cc91a916ed717aed369115140041491b53a22fc2f2967439cace19bc58771a55b6a91b252c0a8f888d6ba1063b0a7c7145d5ee3e866b8f94c3aaa399
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
AllCops:
|
2
2
|
NewCops: enable
|
3
|
-
TargetRubyVersion: 2.
|
3
|
+
TargetRubyVersion: 2.7
|
4
4
|
Exclude:
|
5
5
|
- ext/tulirb/extconf.rb
|
6
6
|
- Gemfile.lock
|
@@ -22,6 +22,7 @@ Metrics/ClassLength:
|
|
22
22
|
Enabled: false
|
23
23
|
Metrics/MethodLength:
|
24
24
|
CountAsOne: ['array', 'heredoc', 'method_call']
|
25
|
+
Max: 30
|
25
26
|
|
26
27
|
|
27
28
|
Layout/LineLength:
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,19 @@
|
|
1
|
-
|
1
|
+
# Tulirb Changelog
|
2
2
|
|
3
|
-
## [
|
3
|
+
## [1.0.0] - 2025-02-24
|
4
4
|
|
5
|
-
-
|
5
|
+
- Fix all compiler warnings in C extension and ensure there are no memory leaks.
|
6
|
+
- [internal] Added AddressSanitizer to detect memory leaks in the C extension.
|
7
|
+
- [internal] Added Dockerfile and docker-compose.yml to run tests in a Linux environment.
|
6
8
|
|
7
|
-
## [0.1.1] -
|
9
|
+
## [0.1.1] - 2024-01-22
|
8
10
|
|
9
11
|
- Fixed ArgumentError messages in C extension.
|
10
12
|
- [interna] Replace metaprogrammed Ruby wrapper methods with explicitly defined methods.
|
11
13
|
- [internal] Added tests for wrapper methods.
|
14
|
+
|
15
|
+
## [0.1.0] - 2023-12-31
|
16
|
+
|
17
|
+
- Initial release
|
18
|
+
|
19
|
+
## [Unreleased]
|
data/Dockerfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
ARG RUBY_VERSION=3.4
|
2
|
+
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim AS base
|
3
|
+
WORKDIR /tulirb
|
4
|
+
COPY . /tulirb
|
5
|
+
RUN apt-get update -qq && \
|
6
|
+
apt-get install -y build-essential \
|
7
|
+
git
|
8
|
+
RUN bundle install
|
9
|
+
CMD [ "/bin/bash" ]
|
10
|
+
ENTRYPOINT [ "/bin/bash", "-c" ]
|
data/README.md
CHANGED
@@ -41,7 +41,11 @@ Find documentation for all indicator functions [here](https://www.rubydoc.info/g
|
|
41
41
|
|
42
42
|
## Development
|
43
43
|
|
44
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
44
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to compile the C extension, detect memory leaks in the C extension and run tests. [AddressSanitizer](https://github.com/google/sanitizers/wiki/addresssanitizer) is used to detect memory leaks in the C extension but it is only available on Linux. I'll recommend running the `rake` command with Docker to run in a Linux environment. There is a Dockerfile and docker-compose.yml file in the root directory to help with this. You can run `rake` with Docker by executing:
|
45
|
+
|
46
|
+
$ docker compose run tulirb rake
|
47
|
+
|
48
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
45
49
|
|
46
50
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
47
51
|
|
data/Rakefile
CHANGED
@@ -1,5 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
def windows?
|
4
|
+
RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
5
|
+
end
|
6
|
+
|
7
|
+
def silence_stream(stream)
|
8
|
+
old_stream = stream.dup
|
9
|
+
stream.reopen(File::NULL)
|
10
|
+
stream.sync = true
|
11
|
+
yield
|
12
|
+
ensure
|
13
|
+
stream.reopen(old_stream)
|
14
|
+
end
|
15
|
+
|
16
|
+
def reenable_task_with_prereqs(task_name)
|
17
|
+
task = Rake::Task[task_name]
|
18
|
+
return unless task.already_invoked # Skip if task hasn't run before
|
19
|
+
|
20
|
+
task.prerequisite_tasks.each { |prereq| reenable_task_with_prereqs(prereq) } # Recursively reset prerequisites
|
21
|
+
task.reenable # Reset the main task itself
|
22
|
+
end
|
23
|
+
|
24
|
+
def detect_memory_leaks!
|
25
|
+
asan_path = `gcc -print-file-name=libasan.so`.strip
|
26
|
+
env = { "LD_PRELOAD" => asan_path }
|
27
|
+
Open3.capture3(env,
|
28
|
+
"ruby -Ilib:ext -r tulirb -e \"puts Tulirb.ema([[2, 4, 6]], period: 5)\"").tap do |_, error, status|
|
29
|
+
puts("Running memory leak detection with AddressSanitizer")
|
30
|
+
unless status.success?
|
31
|
+
errors = error.split("\n\n").grep(%r{ext/tulirb})
|
32
|
+
if errors.any?
|
33
|
+
elog = "Memory leaks detected in C extension:\n\n"
|
34
|
+
elog += errors.join("\n\n")
|
35
|
+
raise(elog)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
puts("No memory leaks detected in C extension")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
3
43
|
require("bundler/gem_tasks")
|
4
44
|
require("rake/testtask")
|
5
45
|
|
@@ -10,15 +50,30 @@ Rake::TestTask.new(:test) do |t|
|
|
10
50
|
end
|
11
51
|
|
12
52
|
require("rubocop/rake_task")
|
13
|
-
|
14
53
|
RuboCop::RakeTask.new
|
15
54
|
|
16
55
|
require("rake/extensiontask")
|
17
|
-
|
18
56
|
task(build: :compile)
|
19
|
-
|
20
57
|
Rake::ExtensionTask.new("tulirb") do |ext|
|
21
58
|
ext.lib_dir = "lib/tulirb"
|
22
59
|
end
|
23
60
|
|
24
|
-
task(
|
61
|
+
task(detect_memory_leaks: :clobber) do
|
62
|
+
next if windows?
|
63
|
+
|
64
|
+
require("open3")
|
65
|
+
ENV["SANITIZE"] = "true"
|
66
|
+
silence_stream($stderr) { Rake::Task["compile"].invoke }
|
67
|
+
|
68
|
+
begin
|
69
|
+
detect_memory_leaks!
|
70
|
+
ensure
|
71
|
+
reenable_task_with_prereqs("clobber")
|
72
|
+
Rake::Task["clobber"].invoke
|
73
|
+
ENV["SANITIZE"] = "false"
|
74
|
+
reenable_task_with_prereqs("clobber")
|
75
|
+
reenable_task_with_prereqs("compile")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
task(default: %i[detect_memory_leaks compile test rubocop clobber])
|
data/docker-compose.yml
ADDED
data/ext/tulirb/extconf.rb
CHANGED
@@ -2,8 +2,12 @@
|
|
2
2
|
|
3
3
|
require "mkmf"
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
if ENV["SANITIZE"] == "true"
|
6
|
+
CONFIG["optflags"] = "-O0"
|
7
|
+
CONFIG["debugflags"] = "-ggdb3"
|
8
|
+
$CFLAGS << " -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls -DTULIRB_SANITIZE=1"
|
9
|
+
$LDFLAGS << " -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
10
|
+
end
|
7
11
|
|
8
12
|
$srcs = %w[tiamalgamation.c tulirb.c]
|
9
13
|
create_makefile("tulirb/tulirb")
|
data/ext/tulirb/tulirb.c
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
#include "tulirb.h"
|
2
2
|
|
3
|
-
|
3
|
+
#ifdef TULIRB_SANITIZE
|
4
|
+
#define TULIRB_MALLOC(size) malloc(size)
|
5
|
+
#define TULIRB_FREE(ptr) free(ptr)
|
6
|
+
#else
|
7
|
+
#define TULIRB_MALLOC(size) xmalloc(size)
|
8
|
+
#define TULIRB_FREE(ptr) xfree(ptr)
|
9
|
+
#endif
|
4
10
|
|
5
|
-
static inline
|
11
|
+
static inline void xfree_ptr_arr(TI_REAL **, size_t);
|
12
|
+
|
13
|
+
static inline VALUE ti_wrapper(VALUE inputs, VALUE opts, const char *indicator_name)
|
6
14
|
{
|
7
15
|
Check_Type(inputs, T_ARRAY);
|
8
16
|
Check_Type(opts, T_ARRAY);
|
@@ -18,9 +26,9 @@ static inline VALUE ti_wrapper(VALUE inputs, VALUE opts, char *indicator_name)
|
|
18
26
|
rb_raise(rb_eArgError, "Invalid options size, expected: %i options", indicator->options);
|
19
27
|
}
|
20
28
|
|
21
|
-
TI_REAL *options = (TI_REAL *)
|
29
|
+
TI_REAL *options = (TI_REAL *)TULIRB_MALLOC(sizeof(TI_REAL[RARRAY_LEN(opts)]));
|
22
30
|
|
23
|
-
for (
|
31
|
+
for (int i = 0; i < RARRAY_LEN(opts); i++)
|
24
32
|
options[i] = NUM2DBL(rb_ary_entry(opts, i));
|
25
33
|
|
26
34
|
const int start = indicator->start(options);
|
@@ -31,26 +39,26 @@ static inline VALUE ti_wrapper(VALUE inputs, VALUE opts, char *indicator_name)
|
|
31
39
|
if (output_length < 0)
|
32
40
|
return Qnil;
|
33
41
|
|
34
|
-
TI_REAL **c_inputs =
|
35
|
-
for (
|
42
|
+
TI_REAL **c_inputs = TULIRB_MALLOC(indicator->inputs * sizeof(TI_REAL *));
|
43
|
+
for (int i = 0; i < indicator->inputs; i++)
|
36
44
|
{
|
37
|
-
TI_REAL *c_entry =
|
45
|
+
TI_REAL *c_entry = TULIRB_MALLOC(sizeof(TI_REAL[size]));
|
38
46
|
VALUE entry = rb_ary_entry(inputs, i);
|
39
|
-
for (
|
47
|
+
for (int j = 0; j < size; j++)
|
40
48
|
{
|
41
49
|
c_entry[j] = NUM2DBL(rb_ary_entry(entry, j));
|
42
50
|
}
|
43
51
|
c_inputs[i] = c_entry;
|
44
52
|
}
|
45
53
|
|
46
|
-
TI_REAL **outputs =
|
47
|
-
for (
|
48
|
-
outputs[i] =
|
54
|
+
TI_REAL **outputs = TULIRB_MALLOC(indicator->outputs * sizeof(TI_REAL *));
|
55
|
+
for (int i = 0; i < indicator->outputs; i++)
|
56
|
+
outputs[i] = TULIRB_MALLOC(sizeof(TI_REAL[output_length]));
|
49
57
|
|
50
58
|
int error = indicator->indicator(size, (const TI_REAL *const *)c_inputs, options, outputs);
|
51
59
|
|
52
60
|
xfree_ptr_arr(c_inputs, indicator->inputs);
|
53
|
-
|
61
|
+
TULIRB_FREE(options);
|
54
62
|
|
55
63
|
if (error == TI_INVALID_OPTION)
|
56
64
|
{
|
@@ -62,7 +70,7 @@ static inline VALUE ti_wrapper(VALUE inputs, VALUE opts, char *indicator_name)
|
|
62
70
|
for (int i = 0; i < indicator->outputs; i++)
|
63
71
|
{
|
64
72
|
VALUE output = rb_ary_new_capa(output_length);
|
65
|
-
for (
|
73
|
+
for (int j = 0; j < output_length; j++)
|
66
74
|
{
|
67
75
|
rb_ary_push(output, DBL2NUM(outputs[i][j]));
|
68
76
|
}
|
@@ -73,11 +81,11 @@ static inline VALUE ti_wrapper(VALUE inputs, VALUE opts, char *indicator_name)
|
|
73
81
|
return ret;
|
74
82
|
}
|
75
83
|
|
76
|
-
static void xfree_ptr_arr(TI_REAL **ptr, size_t size)
|
84
|
+
static inline void xfree_ptr_arr(TI_REAL **ptr, size_t size)
|
77
85
|
{
|
78
|
-
for (
|
79
|
-
|
80
|
-
|
86
|
+
for (size_t i = 0; i < size; i++)
|
87
|
+
TULIRB_FREE(ptr[i]);
|
88
|
+
TULIRB_FREE(ptr);
|
81
89
|
}
|
82
90
|
|
83
91
|
// Alphabetical order
|
@@ -607,7 +615,7 @@ static inline VALUE parameterize(char *str)
|
|
607
615
|
return rb_funcall(rb_str_new2(str), rb_intern("gsub"), 2, rb_str_new2(" "), rb_str_new2("_"));
|
608
616
|
}
|
609
617
|
|
610
|
-
static inline VALUE rb_indicators_info()
|
618
|
+
static inline VALUE rb_indicators_info(void)
|
611
619
|
{
|
612
620
|
VALUE ret = rb_hash_new();
|
613
621
|
for (size_t i = 0; i < TI_INDICATOR_COUNT; i++)
|
@@ -632,7 +640,7 @@ static inline VALUE rb_indicators_info()
|
|
632
640
|
};
|
633
641
|
rb_hash_aset(ret, ID2SYM(rb_intern(indicator.name)), indicators_hash);
|
634
642
|
}
|
635
|
-
return ret;
|
643
|
+
return rb_obj_freeze(ret);
|
636
644
|
};
|
637
645
|
|
638
646
|
void Init_tulirb(void)
|
data/lib/tulirb/version.rb
CHANGED
data/tulirb.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
DESCRIPTION
|
16
16
|
spec.homepage = "https://github.com/ozone4real/tulirb"
|
17
17
|
spec.license = "MIT"
|
18
|
-
spec.required_ruby_version = ">= 2.
|
18
|
+
spec.required_ruby_version = ">= 2.7.0"
|
19
19
|
|
20
20
|
spec.metadata["homepage_uri"] = spec.homepage
|
21
21
|
spec.metadata["source_code_uri"] = "https://github.com/ozone4real/tulirb"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tulirb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ezenwa Ogbonna
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: " Robust technical analysis indicator library for Ruby. \n Build
|
14
14
|
tools for financial trading, data analytics, e.t.c in Ruby\n"
|
@@ -22,11 +22,13 @@ files:
|
|
22
22
|
- ".rubocop.yml"
|
23
23
|
- CHANGELOG.md
|
24
24
|
- CODE_OF_CONDUCT.md
|
25
|
+
- Dockerfile
|
25
26
|
- Gemfile
|
26
27
|
- LICENSE.txt
|
27
28
|
- README.md
|
28
29
|
- Rakefile
|
29
30
|
- code_generator.rb
|
31
|
+
- docker-compose.yml
|
30
32
|
- ext/tulirb/extconf.rb
|
31
33
|
- ext/tulirb/tiamalgamation.c
|
32
34
|
- ext/tulirb/tulirb.c
|
@@ -50,7 +52,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
52
|
requirements:
|
51
53
|
- - ">="
|
52
54
|
- !ruby/object:Gem::Version
|
53
|
-
version: 2.
|
55
|
+
version: 2.7.0
|
54
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
57
|
requirements:
|
56
58
|
- - ">="
|