summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-01-25 11:23:21 +0100
committerGitHub <noreply@github.com>2017-01-25 11:23:21 +0100
commit1e3193ebb2028478aa26efd1b69697cddf00914f (patch)
treed1504a620dc7065f387467162d75276b311ab56c
parent636bfbb768f23c92d581ab660fcaa88927c859b1 (diff)
parent4804cf36ce9a45a0300d5e78850cf55f6b8b2c2c (diff)
Merge pull request #847 from matthiasbeyer/imag-ruby
Imag ruby
-rw-r--r--Cargo.toml1
-rw-r--r--Makefile3
-rw-r--r--libimagruby/.gitignore3
-rw-r--r--libimagruby/Cargo.toml41
-rw-r--r--libimagruby/Gemfile4
-rw-r--r--libimagruby/Makefile20
-rw-r--r--libimagruby/README.md106
-rw-r--r--libimagruby/Rakefile2
-rw-r--r--libimagruby/imag.gemspec26
-rw-r--r--libimagruby/lib/imag.rb96
-rw-r--r--libimagruby/lib/imag/version.rb3
-rw-r--r--libimagruby/src/cache.rs42
-rw-r--r--libimagruby/src/entry.rs282
-rw-r--r--libimagruby/src/imag.rs148
-rw-r--r--libimagruby/src/lib.rs53
-rw-r--r--libimagruby/src/ruby_utils.rs210
-rw-r--r--libimagruby/src/store.rs559
-rw-r--r--libimagruby/src/storeid.rs154
-rw-r--r--libimagruby/src/toml_utils.rs65
-rw-r--r--libimagruby/src/util.rs102
-rw-r--r--libimagruby/test/test_entries.rb27
-rw-r--r--libimagruby/test/test_ruby.rb58
-rw-r--r--libimagruby/test/test_store.rb16
23 files changed, 2021 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
index b12e8164..2a64e19a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,6 +36,7 @@ members = [
"libimagnotes",
"libimagref",
"libimagrt",
+ "libimagruby",
"libimagstore",
"libimagstorestdhook",
"libimagtimeui",
diff --git a/Makefile b/Makefile
index 4a4e751c..e47c0c1d 100644
--- a/Makefile
+++ b/Makefile
@@ -67,6 +67,9 @@ lib: $(LIB_TARGETS)
lib-test: $(LIB_TARGETS_TEST)
+lib-imag-ruby-test:
+ @$(MAKE) -C libimagruby
+
test: bin-test lib-test
install: $(INSTALL_TARGETS) imag-bin-install
diff --git a/libimagruby/.gitignore b/libimagruby/.gitignore
new file mode 100644
index 00000000..2f7c731a
--- /dev/null
+++ b/libimagruby/.gitignore
@@ -0,0 +1,3 @@
+.bundle
+Gemfile.lock
+vendor/bundle
diff --git a/libimagruby/Cargo.toml b/libimagruby/Cargo.toml
new file mode 100644
index 00000000..2d98a086
--- /dev/null
+++ b/libimagruby/Cargo.toml
@@ -0,0 +1,41 @@
+[package]
+name = "libimagruby"
+version = "0.1.0"
+authors = ["Matthias Beyer <mail@beyermatthias.de>"]
+
+description = "Library for the imag core distribution"
+
+keywords = ["imag", "PIM", "personal", "information", "management"]
+readme = "../README.md"
+license = "LGPL-2.1"
+
+documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
+repository = "https://github.com/matthiasbeyer/imag"
+homepage = "http://imag-pim.org"
+
+[lib]
+crate-type = ["dylib"]
+
+[dependencies]
+ruru = "0.9"
+lazy_static = "0.2"
+log = "0.3"
+env_logger = "0.3"
+toml = "0.2"
+uuid = { version = "0.3", features = ["v4"] }
+
+[dependencies.libimagerror]
+path = "../libimagerror"
+
+[dependencies.libimagrt]
+path = "../libimagrt"
+
+[dependencies.libimagstore]
+path = "../libimagstore"
+
+[dependencies.libimagstorestdhook]
+path = "../libimagstorestdhook"
+
+[dependencies.libimagutil]
+path = "../libimagutil"
+
diff --git a/libimagruby/Gemfile b/libimagruby/Gemfile
new file mode 100644
index 00000000..aadb3470
--- /dev/null
+++ b/libimagruby/Gemfile
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+source "https://rubygems.org"
+
+gemspec
diff --git a/libimagruby/Makefile b/libimagruby/Makefile
new file mode 100644
index 00000000..f7ed82d6
--- /dev/null
+++ b/libimagruby/Makefile
@@ -0,0 +1,20 @@
+ECHO=$(shell which echo) -e
+RUBY=$(shell which ruby)
+RUBY_TESTS=$(shell find ./test -maxdepth 1 -name "*.rb" -type f)
+RUBY_TEST_TARGETS=$(foreach x,$(subst ,,$(RUBY_TESTS)),$(x))
+
+all: lib
+
+lib:
+ $(MAKE) -C .. libimagruby
+
+lib-release:
+ $(MAKE) -C .. libimagruby-release
+
+test: lib $(RUBY_TEST_TARGETS)
+
+$(RUBY_TEST_TARGETS): %: lib .FORCE
+ @$(ECHO) "\t[TEST ]:\t$@"
+ @$(RUBY) $(subst -test,,$@)
+
+.FORCE:
diff --git a/libimagruby/README.md b/libimagruby/README.md
new file mode 100644
index 00000000..2f761fb1
--- /dev/null
+++ b/libimagruby/README.md
@@ -0,0 +1,106 @@
+# imag-ruby
+
+A Ruby gem for scripting imag modules.
+
+## How does this work?
+
+Well, as we have some problems with lifetimes here, we have a fairly complex
+codebase in this crate.
+
+### The Problem
+
+The Problem is, that `libimagstore::store::FileLockEntry<'a>` has a lifetime. If
+we would wrap this object into a ruru wrapper and pass to the Ruby code, we
+couldn't guarantee anymore that the lifetime holds.
+
+The problem is simple, you see...
+
+### The solution?
+
+Never pass anything to the Ruby code.
+
+Yes, exactly. The Ruby code only sees 'handles'. It never actually gets the
+`Store` object either.
+We move the `Store` Object into a `Cache` object (actually, the Ruby code could
+have multiple `Store` objects to work with this way) and return a `StoreHandle`
+to the Ruby code (which is a UUID underneath).
+
+Also, the Ruby code never actually touches a `FileLockEntry` - it only gets a
+Handle for each `FileLockEntry` - which is a tuple of the `StoreHandle` and the
+`libimagstore::storeid::StoreId` for the Entry.
+
+Each operation on a `FileLockEntry` is then wrapped by this very library. Each
+time `FileLockEntry` is touched, this library fetches the appropriate `Store`
+object from the static `Cache`, then fetches the `FileLockEntry` object from it,
+does the operation and then drops the object (which implies that the actual
+`FileLockEntry` is `update()`d!).
+
+### The Hell?
+
+Yes, I know this is a lot of overhead. But what are we talking about here? This
+is Ruby code we're talking about here, so speed is not our concern.
+
+You could argue this is a hell of complexity introduced in this library and yes
+it is.
+If there are bugs (and I bet there are) they would be complex as hell.
+But that's it... if you have a better approach, please file a PR.
+
+## Tests?
+
+We have tests Ruby scripts in `./test`, they are not executed by travis-ci, as
+we need Ruby `2.3.0` for this and travis has `2.2.0` as latest version.
+But I hope we get it in travis soonish.
+
+## Ruby gem?
+
+This crate will contain both the Rust bindings for imag using `ruru` and a bunch
+of wrapper code for the actual `imag` gem.
+
+We are not there yet, though.
+
+### Why another layer of indirection?
+
+As "ruru" does not yet support modules (which is sad btw) we would end up with
+functions for all the things.
+
+E.G.: `imag_runtime_setup()` instead of `Imag::Runtime::setup()`
+
+I want to add a Ruby gem to wrap these things.
+
+So basically a piece of Ruby which uses `imag.rb` (the Rust gem) to build
+`imag` as a gem which then exports a fine module system.
+
+### Ideas for module system:
+
+```text
+Imag (Module)
+ Runtime (Class)
+ Store (Class)
+ Entry (Class)
+ EntryHeader (Class)
+ EntryContent (Class (inherits from String))
+ StoreId (Class)
+```
+
+I would name the types the same as in the Rust codebase, to avoid confusion.
+Only exception would be the `Entry` class, which would be a `FileLockEntry`
+underneath.
+
+If we adapt `libimagentrytag` and the other `libimagentry*`
+libraries, we would extend this type.
+
+## More plans
+
+I want to pull these libraries into the Ruby bindings:
+
+* libimagentryedit
+* libimagentryfilter
+* libimagentrylink
+* libimagentrylist
+* libimagentrymarkdown
+* libimagentrytag
+* libimagentryview
+
+Which all provide functions on top of `libimagstore::store::{FileLock,}Entry`,
+so we will implement them on `Imag::Entry`.
+
diff --git a/libimagruby/Rakefile b/libimagruby/Rakefile
new file mode 100644
index 00000000..43022f71
--- /dev/null
+++ b/libimagruby/Rakefile
@@ -0,0 +1,2 @@
+require "bundler/gem_tasks"
+task :default => :spec
diff --git a/libimagruby/imag.gemspec b/libimagruby/imag.gemspec
new file mode 100644
index 00000000..c6066912
--- /dev/null
+++ b/libimagruby/imag.gemspec
@@ -0,0 +1,26 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'imag/version'
+
+Gem::Specification.new do |spec|
+ spec.name = "imag"
+ spec.version = Imag::VERSION
+ spec.authors = ["Matthias Beyer"]
+ spec.email = ["mail@beyermatthias.de"]
+
+ spec.summary = %q{A Ruby gem to script imag.}
+ spec.description = %q{A Ruby gem to script imag, the personal information management suite for the commandline}
+ spec.homepage = "http://imag-pim.org"
+
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
+ f.match(%r{^(test|spec|features)/})
+ end
+
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_development_dependency "bundler", "~> 1.13"
+ spec.add_development_dependency "rake", "~> 10.0"
+end
diff --git a/libimagruby/lib/imag.rb b/libimagruby/lib/imag.rb
new file mode 100644
index 00000000..b8c91115
--- /dev/null
+++ b/libimagruby/lib/imag.rb
@@ -0,0 +1,96 @@
+#!/usr/bin/env ruby
+
+module Imag
+
+ IMAG_INIT_FN_NAME = 'imag_ruby_initialize'
+
+ def self.setup binary_path
+ require binary_path
+
+ self.core_setup
+ self.classes_setup
+ end
+
+ module Logger
+
+ def self.init debug, verbose, color
+ RImag.init_logger debug, verbose, color
+ end
+
+ def self.trace msg
+ RImag.trace msg
+ end
+
+ def self.dbg msg
+ RImag.dbg msg
+ end
+
+ def self.debug msg
+ RImag.debug msg
+ end
+
+ def self.info msg
+ RImag.info msg
+ end
+
+ def self.warn msg
+ RImag.warn msg
+ end
+
+ def self.error msg
+ RImag.error msg
+ end
+
+ end
+
+ private
+
+ def self.class_names
+ [
+ :StoreId ,
+ :StoreHandle ,
+ :FileLockEntryHandle ,
+ :EntryHeader ,
+ :EntryContent ,
+ ]
+ end
+
+ def self.core_setup
+ self.class_names.map {|n| [n, "R#{n}".to_sym ] }.each do |elem|
+ Imag.const_set elem.first, Kernel.const_get(elem.last)
+ end
+ end
+
+ def self.classes_setup
+ self.class_storeid_setup
+ end
+
+ def self.class_storeid_setup
+ Imag::StoreId.class_exec do
+ def to_s
+ self.to_str
+ end
+ end
+ end
+
+end
+
+if __FILE__ == $0
+ puts "Running some tests..."
+ puts "I hope you passed the library object as first argument..."
+ begin
+ Imag.setup ARGV.first
+ rescue Exception => e
+ puts "Seems not to be the case... or something else went wrong..."
+ puts e
+ exit 1
+ end
+
+ Imag::Logger.init true, true, true
+ Imag::Logger.info "The Logger should work now"
+
+ Imag::Logger.info "Lets see whether we have properly setup StoreId"
+ Imag::Logger.info Imag::StoreId::new_baseless("baselessId").to_s
+ Imag::Logger.info "Seems good."
+end
+
diff --git a/libimagruby/lib/imag/version.rb b/libimagruby/lib/imag/version.rb
new file mode 100644
index 00000000..c2a2473f
--- /dev/null
+++ b/libimagruby/lib/imag/version.rb
@@ -0,0 +1,3 @@
+module Imag
+ VERSION = "0.1.0"
+end
diff --git a/libimagruby/src/cache.rs b/libimagruby/src/cache.rs
new file mode 100644
index 00000000..94c2b869
--- /dev/null
+++ b/libimagruby/src/cache.rs
@@ -0,0 +1,42 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; version
+// 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use std::sync::Mutex;
+
+use uuid::Uuid;
+
+use libimagstore::store::Store;
+
+#[derive(Clone, Debug, Ord, Hash, Eq, PartialOrd, PartialEq)]
+pub struct StoreHandle(Uuid);
+
+impl StoreHandle {
+ pub fn new() -> StoreHandle {
+ StoreHandle(Uuid::new_v4())
+ }
+}
+
+lazy_static! {
+ pub static ref RUBY_STORE_CACHE: Arc<Mutex<BTreeMap<StoreHandle, Store>>> = {
+ Arc::new(Mutex::new(BTreeMap::new()))
+ };
+}
+
diff --git a/libimagruby/src/entry.rs b/libimagruby/src/entry.rs
new file mode 100644
index 00000000..4614feab
--- /dev/null
+++ b/libimagruby/src/entry.rs
@@ -0,0 +1,282 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; version
+// 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+use std::collections::BTreeMap;
+use std::error::Error;
+use std::ops::Deref;
+use std::ops::DerefMut;
+
+use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject};
+use uuid::Uuid;
+
+use libimagstore::store::FileLockEntry as FLE;
+use libimagstore::store::EntryHeader;
+use libimagstore::store::EntryContent;
+use libimagstore::store::Entry;
+use libimagstore::storeid::StoreId;
+
+use ruby_utils::IntoToml;
+use toml_utils::IntoRuby;
+use util::Wrap;
+use util::Unwrap;
+use cache::RUBY_STORE_CACHE;
+use cache::StoreHandle;
+
+pub struct FileLockEntryHandle(StoreHandle, StoreId);
+
+impl FileLockEntryHandle {
+ pub fn new(sh: StoreHandle, id: StoreId) -> FileLockEntryHandle {
+ FileLockEntryHandle(sh, id)
+ }
+
+ pub fn store_handle(&self) -> &StoreHandle {
+ &self.0
+ }
+
+ pub fn fle_handle(&self) -> &StoreId {
+ &self.1
+ }
+}
+
+wrappable_struct!(FileLockEntryHandle, FileLockEntryWrapper, FLE_WRAPPER);
+class!(RFileLockEntryHandle);
+impl_wrap!(FileLockEntryHandle => FLE_WRAPPER);
+impl_unwrap!(RFileLockEntryHandle => FileLockEntryHandle => FLE_WRAPPER);
+impl_verified_object!(RFileLockEntryHandle);
+
+/// Helper macro for operating on RUBY_STORE_CACHE object
+///
+/// This helps us calling operations on FileLockEntry objects.
+///
+/// What I do here: Fetch the Store object from the cache, fetch the appropriate FileLockEntry and
+/// call the operation on it.
+///
+/// This could be improved with another cache, so not the store is cached but the FileLockEntry
+/// only, but then we run into lifetime problems with the Store and its FileLockEntry objects.
+/// Feel free to fix this, but for now, this is a workable solution.
+///
+#[macro_export]
+macro_rules! call_on_fle_from_store {
+ ($itself:ident ($wrapper:ident) -> $name:ident -> $operation:block) => {{
+ let handle = $itself.get_data(&*$wrapper);
+ let store_handle = handle.store_handle();
+ call_on_store_by_handle! {
+ store_handle named store inside {
+ match store.get(handle.fle_handle().clone()) {
+ Ok(Some(mut $name)) => {
+ $operation
+ },
+ Ok(None) => {
+ VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
+ NilClass::new().to_any_object()
+ },
+ Err(e) => {
+ VM::raise(Class::from_existing("RuntimeError"), e.description());
+ NilClass::new().to_any_object()
+ },
+ }
+ }
+ }
+ }};
+ ($itself:ident ($wrapper:ident) -> $name:ident -> $operation: block on fail return $ex:expr) => {{
+ let handle = $itself.get_data(&*$wrapper);
+ let store_handle = handle.store_handle();
+ call_on_store_by_handle! {
+ store_handle named store inside {
+ match store.get(handle.fle_handle().clone()) {
+ Ok(Some(mut $name)) => {
+ $operation
+ },
+ Ok(None) => {
+ VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
+ $ex
+ },
+ Err(e) => {
+ VM::raise(Class::from_existing("RuntimeError"), e.description());
+ $ex
+ },
+ }
+ }
+ }
+ }};
+}
+
+
+methods!(
+ RFileLockEntryHandle,
+ itself,
+
+ fn r_get_location() -> AnyObject {
+ call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_location().clone().wrap() })
+ }
+
+ fn r_get_header() -> AnyObject {
+ call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_header().clone().wrap() })
+ }
+
+ fn r_set_header(hdr: Hash) -> NilClass {
+ use ruby_utils::IntoToml;
+ use toml_utils::IntoRuby;
+ use toml::Value;
+
+ let entryheader = match typecheck!(hdr or return NilClass::new()).into_toml() {
+ Value::Table(t) => EntryHeader::from(t),
+ _ => {
+ let ec = Class::from_existing("RuntimeError");
+ VM::raise(ec, "Something weird happened. Hash seems to be not a Hash");
+ return NilClass::new();
+ },
+ };
+
+ call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
+ *fle.get_header_mut() = entryheader;
+ NilClass::new().to_any_object()
+ });
+
+ NilClass::new()
+ }
+
+ fn r_get_content() -> AnyObject {
+ call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
+ fle.get_content().clone().wrap()
+ } on fail return NilClass::new().to_any_object())
+ }
+
+ fn r_set_content(ctt: RString) -> NilClass {
+ use ruby_utils::IntoToml;
+ use toml_utils::IntoRuby;
+ use toml::Value;
+
+ let content = match typecheck!(ctt).into_toml() {
+ Value::String(s) => s,
+ _ => {
+ let ec = Class::from_existing("RuntimeError");
+ VM::raise(ec, "Something weird happened. String seems to be not a String");
+ return NilClass::new();
+ },
+ };
+
+ call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
+ *fle.get_content_mut() = content;
+ NilClass::new().to_any_object()
+ });
+
+ NilClass::new()
+ }
+
+);
+
+wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
+class!(REntryHeader);
+impl_wrap!(EntryHeader => ENTRY_HEADER_WRAPPER);
+impl_unwrap!(REntryHeader => EntryHeader => ENTRY_HEADER_WRAPPER);
+impl_verified_object!(REntryHeader);
+
+methods!(
+ REntryHeader,
+ itself,
+
+ fn r_entry_header_new() -> AnyObject {
+ EntryHeader::new().wrap()
+ }
+
+ fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
+ let spec = typecheck!(spec or return Boolean::new(false)).to_string();
+ let obj = obj.unwrap(); // possibly not safe... TODO
+
+ match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
+ Ok(b) => Boolean::new(b),
+ Err(e) => {
+ VM::raise(Class::from_existing("RuntimeError"), e.description());
+ Boolean::new(false)
+ }
+ }
+ }
+
+ fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
+ use ruru::NilClass;
+
+ let spec = typecheck!(spec or return any Boolean::new(false)).to_string();
+ let obj = obj.unwrap(); // possibly not safe... TODO
+
+ match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
+ Ok(Some(v)) => v.into_ruby(),
+ Ok(None) => NilClass::new().to_any_object(),
+ Err(e) => {
+ VM::raise(Class::from_existing("RuntimeError"), e.description());
+ return Boolean::new(false).to_any_object();
+ }
+ }
+ }
+
+ fn r_entry_header_get(spec: RString) -> AnyObject {
+ use ruru::NilClass;
+
+ let spec = typecheck!(spec or return any Boolean::new(false)).to_string();
+
+ match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
+ Ok(Some(v)) => v.into_ruby(),
+ Ok(None) => NilClass::new().to_any_object(),
+ Err(e) => {
+ VM::raise(Class::from_existing("RuntimeError"), e.description());
+ return Boolean::new(false).to_any_object();
+ }
+ }
+ }
+
+);
+
+wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
+class!(REntryContent);
+impl_wrap!(EntryContent => ENTRY_CONTENT_WRAPPER);
+impl_unwrap!(REntryContent => EntryContent => ENTRY_CONTENT_WRAPPER);
+
+wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
+class!(REntry);
+impl_unwrap!(REntry => Entry => ENTRY_WRAPPER);
+
+pub fn setup_filelockentry() -> Class {
+ let mut class = Class::new("RFileLockEntryHandle", None);
+ class.define(|itself| {
+ itself.def("location", r_get_location);
+ itself.def("header" , r_get_header);
+ itself.def("header=" , r_set_header);
+ itself.def("content" , r_get_content);
+ itself.def("content=", r_set_content);
+ });
+ class
+}
+
+pub fn setup_entryheader() -> Class {
+ let mut class = Class::new("REntryHeader", None);
+ class.define(|itself| {
+ itself.def("insert", r_entry_header_insert);
+ itself.def("set" , r_entry_header_set);
+ itself.def("[]=" , r_entry_header_set);
+ itself.def("read" , r_entry_header_get);
+ itself.def("[]" , r_entry_header_get);
+ });
+ class
+}
+
+pub fn setup_entrycontent() -> Class {
+ let string = Class::from_existing("String");
+ let mut class = Class::new("REntryContent", Some(&string));
+ class
+}
diff --git a/libimagruby/src/imag.rs b/libimagruby/src/imag.rs
new file mode 100644
index 00000000..a59610b2
--- /dev/null
+++ b/libimagruby/src/imag.rs
@@ -0,0 +1,148 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; version
+// 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+
+use std::error::Error;
+
+use ruru::{Class, Boolean, RString, NilClass, VM, Object};
+
+use libimagrt::logger::ImagLogger;
+
+class!(RImag);
+
+methods!(
+ RImag,
+ itself,
+
+ fn r_initialize_logger(debug: Boolean, verbose: Boolean, colored: Boolean) -> NilClass {
+ use std::env::var as env_var;
+ use env_logger;
+ use log;
+ use log::{Log, LogLevel, LogRecord, LogMetadata};
+ use log::LogLevelFilter;
+
+ let debug = match debug {
+ Ok(d) => d.to_bool(),
+ Err(ref e) => {
+ VM::raise(e.to_exception(), e.description());
+ return NilClass::new();
+ },
+ };
+
+ let verbose = match verbose {
+ Ok(v) => v.to_bool(),
+ Err(ref e) => {
+ VM::raise(e.to_exception(), e.description());
+ return NilClass::new();
+ },
+ };
+
+ let colored = match colored {
+ Ok(c) => c.to_bool(),
+ Err(ref e) => {
+ VM::raise(e.to_exception(), e.description());
+ return NilClass::new();
+ },
+ };
+
+ if env_var("IMAG_LOG_ENV").is_ok() {
+ env_logger::init().unwrap();
+ } else {
+ let lvl = if debug {
+ LogLevelFilter::Debug
+ } else if verbose {
+ LogLevelFilter::Info
+ } else {
+ LogLevelFilter::Warn
+ };
+
+ log::set_logger(|max_log_lvl| {
+ max_log_lvl.set(lvl);
+ debug!("Init logger with {}", lvl);
+ let lgr = ImagLogger::new(lvl.to_log_level().unwrap())
+ .with_color(colored)
+ .with_prefix("[imag][ruby]".to_owned())
+ .with_dbg_file_and_line(false);
+ Box::new(lgr)
+ })
+ .map_err(|_| {
+ panic!("Could not setup logger");
+ })
+ .ok();
+ }
+
+ NilClass::new()
+ }
+
+ fn r_log_trace(l: RString) -> NilClass {
+ match l {
+ Err(ref e) => VM::raise(e.to_exception(), e.description()),
+ Ok(s) => trace!("{}", s.to_string()),
+ }
+ NilClass::new()
+ }
+
+ fn r_log_debug(l: RString) -> NilClass {
+ match l {
+ Err(ref e) => VM::raise(e.to_exception(), e.description()),
+ Ok(s) => debug!("{}", s.to_string()),
+ }
+ NilClass::new()
+ }
+
+ fn r_log_info(l: RString) -> NilClass {
+ match l {
+ Err(ref e) => VM::raise(e.to_exception(), e.description()),
+ Ok(s) => info!("{}", s.to_string()),
+ }
+ NilClass::new()
+ }
+
+ fn r_log_warn(l: RString) -> NilClass {
+ match l {
+ Err(ref e) => VM::raise(e.to_exception(), e.description()),
+ Ok(s) => warn!("{}", s.to_string()),
+ }
+ NilClass::new()
+ }
+
+ fn r_log_error(l: RString) -> NilClass {
+ match l {
+ Err(ref e) => VM::raise(e.to_exception(), e.description()),
+ Ok(s) => error!("{}", s.to_string()),
+ }
+ NilClass::new()
+ }
+
+);
+
+pub fn setup() -> Class {
+ let mut class = Class::new("RImag", None);
+ class.define(|itself| {
+ itself.def_self("init_logger", r_initialize_logger);
+ itself.def_self("trace", r_log_trace);
+ itself.def_self("dbg", r_log_debug);
+ itself.def_self("debug", r_log_debug);
+ itself.def_self("info", r_log_info);
+ itself.def_self("warn", r_log_warn);
+ itself.def_self("error", r_log_error);
+ });
+ class
+}
+
diff --git a/libimagruby/src/lib.rs b/libimagruby/src/lib.rs
new file mode 100644
index 00000000..13d933b3
--- /dev/null
+++ b/libimagruby/src/lib.rs
@@ -0,0 +1,53 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; version
+// 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+#[macro_use] extern crate ruru;
+#[macro_use] extern crate lazy_static;
+#[macro_use] extern crate log;
+extern crate env_logger;
+extern crate toml;
+extern crate uuid;
+
+#[macro_use] extern crate libimagerror;
+extern crate libimagstore;
+extern crate libimagstorestdhook;
+extern crate