kalimba 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 51492726fdd0d0303d00af8376c1bef0af951d48
4
+ data.tar.gz: c4bdf88223e9efd0591d447a152b9f16896d0626
5
+ SHA512:
6
+ metadata.gz: 1543ecdd70a064ca87f4328d5e6729297d28cc93174917c2907911512191746b28c0bff95bcff08fe190956522bb6926c515231541e986ce0bc6ccb04af5304e
7
+ data.tar.gz: 61c0b9a9a146b8da73499ea763b21a02e2405fda113c2dd8ba589479f126d61ea9bffaadda60e17fffe0bf71768732f85a64ddff88b653f86be84ae3278cecc7
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .rspec
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ TAGS
20
+ *~
21
+ .*~
22
+ bin/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec name: "kalimba"
4
+ gemspec name: "kalimba-redlander"
5
+
6
+ gem "rake"
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Slava Kravchenko
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,132 @@
1
+ # Kalimba
2
+
3
+ Kalimba is an clone of ActiveRecord, based on ActiveModel framework.
4
+ Combined with the raw power of Redlander gem, it introduces the world of Ruby on Rails
5
+ to the world of RDF, triple storages, LinkedData and Semantic Web.
6
+ The resources of semantic graph storages become accessible in a customary form of "models".
7
+
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'kalimba'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install kalimba
22
+
23
+
24
+ ## Backends
25
+
26
+ You won't be able to do much without a backend to handle your RDF statements.
27
+ Please add "kalimba-redlander" gem dependency to your Gemfile, and make sure
28
+ to "require 'kalimba-redlander'" before invoking "require 'kalimba'".
29
+
30
+ For now, the backends are developed as a part of Kalimba gem for convenience.
31
+ However, you are free to develop your own backend as a separate gem.
32
+
33
+
34
+ ### Kalimba::Persistence::Redlander
35
+
36
+ Redlander adapter for [Kalimba](https://github.com/cordawyn/kalimba). It provides the RDF storage backend for Kalimba.
37
+
38
+
39
+ ## Usage
40
+
41
+ Your model must be inherited from Kalimba::Resource:
42
+
43
+ class Person < Kalimba::Resource
44
+ # Note that type is *not* inherited,
45
+ # it must be explicitly declared for every subclass.
46
+ # And types better be unique!
47
+ type "http://schema.org/Person"
48
+
49
+ # Define base URI for the instances of this resource
50
+ base_uri "http://example.org/people"
51
+
52
+ property :name, :predicate => NS::FOAF["name"], :datatype => NS:XMLSchema["string"]
53
+
54
+ has_many :friends, :predicate => "http://schema.org/Person", :datatype => :Person
55
+ end
56
+
57
+ From this point on, you may treat your model just like
58
+ any fully-fledged clone of ActiveModel (i.e. ActiveRecord model)
59
+
60
+ $ alice = Person.new(:name => "Alice")
61
+ $ alice.valid?
62
+ $ alice.save!
63
+ ...
64
+ $ alice.friends << bob
65
+
66
+ > Note that Kalimba associations are not fully API-compliant with ActiveRecord associations (yet?).
67
+ > One major feature missing is "association proxy" which would enable tricks like
68
+ > `alice.friends.destroy_all`. Presently, Kalimba "associations" return a simple collection (Array).
69
+
70
+ For other details refer to YARD documentation for Kalimba::Resource module.
71
+
72
+
73
+ ## Regarding RDFS/OWL features
74
+
75
+ It should be also noted that "special" features of RDFS/OWL like inverse properties or
76
+ transitive properties and so on, are *not* specifically handled by Kalimba (or Redlander backend).
77
+ Availability of any "virtual" data which is supposed to be available as a product of reasoning,
78
+ is up to the graph storage that you use with the *backend*.
79
+
80
+ So (provided that "hasFriend" is "owl:inverseOf" "isFriendOf") you may end with something like this:
81
+
82
+ alice.has_friend # => bob
83
+ bob.is_friend_of # => nil
84
+
85
+ ... unless your graph storage provides reasoning by default.
86
+
87
+ That said, certain graph storages that are said to have reasoning capabilities,
88
+ do not have reasoning enabled by default (e.g. [Virtuoso](http://virtuoso.openlinksw.com/)),
89
+ and require that you explicitly enable it using special options or a custom SPARQL syntax.
90
+ While it is possible to "hack" and modify the options or SPARQL queries that are generated
91
+ by Kalimba (or its backend), this is not currently available.
92
+
93
+
94
+ ## Validations
95
+
96
+ For details, refer to ActionModel::Validations documentation.
97
+
98
+ class Human < Kalimba::Resource
99
+ base_uri "http://example.com/people/"
100
+ property :name, :predicate => NS::FOAF["name"], :datatype => NS:XMLSchema["string"]
101
+
102
+ validates_presence_of :name
103
+ end
104
+
105
+ $ bob = Human.create # => bob will have an error on :name
106
+
107
+ ## Callbacks
108
+
109
+ Kalimba supports :before, :after and :around callbacks for :save, :create, :update and
110
+ :destroy actions.
111
+
112
+ class Human < Kalimba::Resource
113
+ base_uri "http://example.com/people/"
114
+
115
+ before_save :shout
116
+
117
+ private
118
+
119
+ def shout
120
+ puts "Hey!"
121
+ end
122
+ end
123
+
124
+ For details, refer to ActionModel::Callbacks documentation.
125
+
126
+ ## Contributing
127
+
128
+ 1. Fork it
129
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
130
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
131
+ 4. Push to the branch (`git push origin my-new-feature`)
132
+ 5. Create new Pull Request
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_helper'
4
+ Bundler::GemHelper.install_tasks(name: "kalimba")
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../lib/kalimba/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "kalimba-redlander"
5
+ gem.version = Kalimba::VERSION
6
+ gem.authors = ["Slava Kravchenko"]
7
+ gem.email = ["slava.kravchenko@gmail.com"]
8
+ gem.description = %q{Redlander adapter for Kalimba. It provides the RDF storage backend for Kalimba.}
9
+ gem.summary = %q{Redlander adapter for Kalimba}
10
+ gem.homepage = "https://github.com/cordawyn/kalimba-redlander"
11
+
12
+ gem.files = ["lib/kalimba/persistence/redlander.rb"]
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.require_paths = ["lib"]
16
+
17
+ gem.add_runtime_dependency "kalimba"
18
+ gem.add_runtime_dependency "redlander", "~> 0.6.0"
19
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path('../lib/kalimba/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Slava Kravchenko"]
5
+ gem.email = ["slava.kravchenko@gmail.com"]
6
+ gem.description = %q{ActiveModel-based framework, which allows the developer to combine RDF resources into ActiveRecord-like models.}
7
+ gem.summary = %q{Kalimba provides ActiveRecord-like capabilities for RDF resources.}
8
+ gem.homepage = "https://github.com/cordawyn/kalimba"
9
+
10
+ gem.files = `git ls-files`.split($\) - ["lib/kalimba/persistence/redlander.rb"]
11
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
12
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
+ gem.name = "kalimba"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = Kalimba::VERSION
16
+
17
+ gem.add_runtime_dependency "activemodel", "~> 3.2"
18
+ gem.add_runtime_dependency "activesupport", "~> 3.2"
19
+
20
+ gem.add_development_dependency "rspec", "~> 2.11.0"
21
+ gem.add_development_dependency "kalimba-redlander"
22
+ end
@@ -0,0 +1,24 @@
1
+ require "set"
2
+ require "active_model" # TODO: not all is required?
3
+
4
+ require "kalimba/version"
5
+ require "kalimba/exceptions"
6
+ require "kalimba/resource"
7
+
8
+ module Kalimba
9
+ class << self
10
+ def repository
11
+ @repository ||= Persistence.repository(@repository_options || {})
12
+ end
13
+
14
+ # Set repository options
15
+ #
16
+ # @param [Hash] options options to be passed to the repository constructor
17
+ # @return [void]
18
+ def set_repository_options(options = {})
19
+ @repository_options = options
20
+ end
21
+ end
22
+ end
23
+
24
+ require "kalimba/railtie" if defined?(Rails)
@@ -0,0 +1,148 @@
1
+ module Kalimba
2
+ module AttributeAssignment
3
+ # Assign attributes from the given hash
4
+ #
5
+ # @param [Hash<[Symbol, String] => Any>] new_attributes
6
+ # @param [Hash] options
7
+ # @return [void]
8
+ def assign_attributes(new_attributes = {}, options = {})
9
+ return if new_attributes.blank?
10
+
11
+ attributes = new_attributes.stringify_keys
12
+ multi_parameter_attributes = []
13
+ nested_parameter_attributes = []
14
+
15
+ attributes.each do |k, v|
16
+ if k.include?("(")
17
+ multi_parameter_attributes << [ k, v ]
18
+ elsif respond_to?("#{k}=")
19
+ if v.is_a?(Hash)
20
+ nested_parameter_attributes << [ k, v ]
21
+ else
22
+ send("#{k}=", v)
23
+ end
24
+ else
25
+ raise UnknownAttributeError, "unknown attribute: #{k}"
26
+ end
27
+ end
28
+
29
+ # assign any deferred nested attributes after the base attributes have been set
30
+ nested_parameter_attributes.each do |k,v|
31
+ send("#{k}=", v)
32
+ end
33
+
34
+ assign_multiparameter_attributes(multi_parameter_attributes)
35
+ end
36
+
37
+ private
38
+
39
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
40
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
41
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
42
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
43
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
44
+ # f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
45
+ # attribute will be set to nil.
46
+ def assign_multiparameter_attributes(pairs)
47
+ execute_callstack_for_multiparameter_attributes(
48
+ extract_callstack_for_multiparameter_attributes(pairs)
49
+ )
50
+ end
51
+
52
+ def execute_callstack_for_multiparameter_attributes(callstack)
53
+ errors = []
54
+ callstack.each do |name, values_with_empty_parameters|
55
+ begin
56
+ send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
57
+ rescue => ex
58
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
59
+ end
60
+ end
61
+ unless errors.empty?
62
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
63
+ end
64
+ end
65
+
66
+ def read_value_from_parameter(name, values_hash_from_param)
67
+ case self.class.properties[name][:datatype]
68
+ when NS::XMLSchema["string"]
69
+ read_other_parameter_value(LocalizedString, name, values_hash_from_param)
70
+ when NS::XMLSchema["dateTime"], NS::XMLSchema["time"]
71
+ read_time_parameter_value(name, values_hash_from_param)
72
+ when NS::XMLSchema["date"]
73
+ read_date_parameter_value(name, values_hash_from_param)
74
+ else
75
+ values_hash_from_param
76
+ end
77
+ end
78
+
79
+ def read_other_parameter_value(klass, name, values_hash_from_param)
80
+ max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param)
81
+ values = (1..max_position).collect do |position|
82
+ raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
83
+ values_hash_from_param[position]
84
+ end
85
+ klass.new(*values)
86
+ end
87
+
88
+ def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100)
89
+ [values_hash_from_param.keys.max,upper_cap].min
90
+ end
91
+
92
+ def extract_callstack_for_multiparameter_attributes(pairs)
93
+ pairs.inject({}) do |attributes, (multiparameter_name, value)|
94
+ attribute_name = multiparameter_name.split("(").first
95
+ attributes[attribute_name] = {} unless attributes.include?(attribute_name)
96
+
97
+ parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
98
+ attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
99
+ attributes
100
+ end
101
+ end
102
+
103
+ def type_cast_attribute_value(multiparameter_name, value)
104
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
105
+ end
106
+
107
+ def find_parameter_position(multiparameter_name)
108
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
109
+ end
110
+
111
+ def read_time_parameter_value(name, values_hash_from_param)
112
+ if values_hash_from_param.size == 2
113
+ instantiate_time_using_two_fields(name, values_hash_from_param)
114
+ else
115
+ instantiate_time_using_many_fields(name, values_hash_from_param)
116
+ end
117
+ end
118
+
119
+ def read_date_parameter_value(name, values_hash_from_param)
120
+ return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
121
+ set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]]
122
+ begin
123
+ Date.new(*set_values)
124
+ rescue ArgumentError # if Date.new raises an exception on an invalid date
125
+ # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
126
+ Time.new(*set_values).to_date
127
+ end
128
+ end
129
+
130
+ def instantiate_time_using_two_fields(name, values_hash_from_param)
131
+ value = [values_hash_from_param[1], values_hash_from_param[2]].join(" ")
132
+ Time.parse(value) rescue nil
133
+ end
134
+
135
+ def instantiate_time_using_many_fields(name, values_hash_from_param)
136
+ # If Date bits were not provided, error
137
+ raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
138
+ max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
139
+ # If Date bits were provided but blank, then return nil
140
+ return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
141
+
142
+ set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
143
+ # If Time bits are not there, then default to 0
144
+ (3..5).each {|i| set_values[i] = set_values[i].blank? ? 0 : set_values[i]}
145
+ Time.new(*set_values)
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,30 @@
1
+ require "active_model/callbacks"
2
+
3
+ module Kalimba
4
+ module Callbacks
5
+ def destroy
6
+ run_callbacks :destroy do
7
+ super
8
+ end
9
+ end
10
+
11
+ def save(options = {})
12
+ run_callbacks :save do
13
+ persistence_callback_type = new_record? ? :create : :update
14
+ run_callbacks persistence_callback_type do
15
+ super
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def self.included(klass)
23
+ super
24
+ klass.class_eval do
25
+ extend ActiveModel::Callbacks
26
+ define_model_callbacks :create, :update, :destroy, :save
27
+ end
28
+ end
29
+ end
30
+ end