vagas-orientdb4r 0.5.2

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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +26 -0
  4. data/.travis.yml +18 -0
  5. data/Gemfile +9 -0
  6. data/LICENSE +202 -0
  7. data/README.rdoc +124 -0
  8. data/Rakefile +43 -0
  9. data/changelog.txt +60 -0
  10. data/ci/initialize-ci.sh +36 -0
  11. data/fstudy/design_v1.dia +0 -0
  12. data/fstudy/design_v1.png +0 -0
  13. data/fstudy/domain_model.dia +0 -0
  14. data/fstudy/domain_model.png +0 -0
  15. data/fstudy/flat_class_perf.rb +56 -0
  16. data/fstudy/sample1_object_diagram.dia +0 -0
  17. data/fstudy/sample1_object_diagram.png +0 -0
  18. data/fstudy/study_case.rb +87 -0
  19. data/fstudy/technical_feasibility.rb +256 -0
  20. data/lib/orientdb4r.rb +115 -0
  21. data/lib/orientdb4r/bin/client.rb +86 -0
  22. data/lib/orientdb4r/bin/connection.rb +29 -0
  23. data/lib/orientdb4r/bin/constants.rb +20 -0
  24. data/lib/orientdb4r/bin/io.rb +38 -0
  25. data/lib/orientdb4r/bin/protocol28.rb +101 -0
  26. data/lib/orientdb4r/bin/protocol_factory.rb +25 -0
  27. data/lib/orientdb4r/chained_error.rb +37 -0
  28. data/lib/orientdb4r/client.rb +364 -0
  29. data/lib/orientdb4r/load_balancing.rb +113 -0
  30. data/lib/orientdb4r/node.rb +42 -0
  31. data/lib/orientdb4r/rest/client.rb +517 -0
  32. data/lib/orientdb4r/rest/excon_node.rb +115 -0
  33. data/lib/orientdb4r/rest/model.rb +159 -0
  34. data/lib/orientdb4r/rest/node.rb +43 -0
  35. data/lib/orientdb4r/rest/restclient_node.rb +77 -0
  36. data/lib/orientdb4r/rid.rb +54 -0
  37. data/lib/orientdb4r/utils.rb +203 -0
  38. data/lib/orientdb4r/version.rb +39 -0
  39. data/orientdb4r.gemspec +37 -0
  40. data/test/bin/test_client.rb +21 -0
  41. data/test/readme_sample.rb +38 -0
  42. data/test/test_client.rb +93 -0
  43. data/test/test_database.rb +261 -0
  44. data/test/test_ddo.rb +237 -0
  45. data/test/test_dmo.rb +115 -0
  46. data/test/test_document_crud.rb +184 -0
  47. data/test/test_gremlin.rb +52 -0
  48. data/test/test_helper.rb +10 -0
  49. data/test/test_loadbalancing.rb +81 -0
  50. data/test/test_utils.rb +67 -0
  51. metadata +136 -0
@@ -0,0 +1,115 @@
1
+ require 'excon'
2
+
3
+ module Orientdb4r
4
+
5
+ ###
6
+ # This class represents a single sever/node in the Distributed Multi-Master Architecture
7
+ # accessible view REST API and 'excon' library on the client side.
8
+ class ExconNode < RestNode
9
+
10
+ attr_accessor :proxy
11
+
12
+ def request(options) #:nodoc:
13
+ verify_options(options, {:user => :mandatory, :password => :mandatory, \
14
+ :uri => :mandatory, :method => :mandatory, :content_type => :optional, :data => :optional,
15
+ :no_session => :optional})
16
+
17
+ opts = options.clone # if not cloned we change original hash map that cannot be used more with load balancing
18
+
19
+ # Auth + Cookie + Content-Type
20
+ opts[:headers] = headers(opts)
21
+ opts.delete :user
22
+ opts.delete :password
23
+ use_session = !opts.delete(:no_session)
24
+
25
+ opts[:body] = opts[:data] if opts.include? :data # just other naming convention
26
+ opts.delete :data
27
+ opts[:path] = opts[:uri] if opts.include? :uri # just other naming convention
28
+ opts.delete :uri
29
+
30
+ was_ok = false
31
+ begin
32
+ response = connection.request opts
33
+ was_ok = (2 == (response.status / 100))
34
+
35
+ # store session ID if received to reuse in next request
36
+ cookies = CGI::Cookie::parse(response.headers['Set-Cookie'])
37
+ sessid = cookies[SESSION_COOKIE_NAME][0]
38
+ if session_id != sessid and use_session
39
+ @session_id = sessid
40
+ Orientdb4r::logger.debug "new session id: #{session_id}"
41
+ end
42
+
43
+ def response.code
44
+ status
45
+ end
46
+
47
+ rescue Excon::Errors::SocketError
48
+ raise NodeError
49
+ end
50
+
51
+ # this is workaround for a strange behavior:
52
+ # excon delivered magic response status '1' when previous request was not 20x
53
+ unless was_ok
54
+ connection.reset
55
+ Orientdb4r::logger.debug 'response code not 20x -> connection reset'
56
+ end
57
+
58
+ response
59
+ end
60
+
61
+
62
+ def post_connect(user, password, http_response) #:nodoc:
63
+
64
+ cookies = CGI::Cookie::parse(http_response.headers['Set-Cookie'])
65
+ @session_id = cookies[SESSION_COOKIE_NAME][0]
66
+
67
+ end
68
+
69
+
70
+ def cleanup #:nodoc:
71
+ super
72
+ connection.reset
73
+ @connection = nil
74
+ end
75
+
76
+
77
+ # ---------------------------------------------------------- Assistant Stuff
78
+
79
+ private
80
+
81
+ ###
82
+ # Gets Excon connection.
83
+ def connection
84
+ return @connection unless @connection.nil?
85
+
86
+ options = {}
87
+ options[:proxy] = proxy unless proxy.nil?
88
+ options[:scheme], options[:host], options[:port]=url.scan(/(.*):\/\/(.*):([0-9]*)/).first
89
+
90
+ @connection ||= Excon::Connection.new(options)
91
+ #:read_timeout => self.class.read_timeout,
92
+ #:write_timeout => self.class.write_timeout,
93
+ #:connect_timeout => self.class.connect_timeout
94
+ end
95
+
96
+ ###
97
+ # Get request headers prepared with session ID and Basic Auth.
98
+ def headers(options)
99
+ rslt = {'Authorization' => basic_auth_header(options[:user], options[:password])}
100
+ rslt['Cookie'] = "#{SESSION_COOKIE_NAME}=#{session_id}" if !session_id.nil? and !options[:no_session]
101
+ rslt['Content-Type'] = options[:content_type] if options.include? :content_type
102
+ rslt['User-Agent'] = user_agent unless user_agent.nil?
103
+ rslt
104
+ end
105
+
106
+ ###
107
+ # Gets value of the Basic Auth header.
108
+ def basic_auth_header(user, password)
109
+ b64 = Base64.encode64("#{user}:#{password}").delete("\r\n")
110
+ "Basic #{b64}"
111
+ end
112
+
113
+ end
114
+
115
+ end
@@ -0,0 +1,159 @@
1
+ module Orientdb4r
2
+
3
+
4
+ ###
5
+ # Extends a Hash produced by JSON.parse.
6
+ module HashExtension
7
+
8
+ ###
9
+ # Gets an attribute value that has to be presented.
10
+ def get_mandatory_attribute(name)
11
+ key = name.to_s
12
+ raise ::ArgumentError, "unknown attribute, name=#{key}" unless self.include? key
13
+ self[key]
14
+ end
15
+
16
+ end
17
+
18
+
19
+ ###
20
+ # This module represents API to OrientDB's class.
21
+ module OClass
22
+
23
+ ###
24
+ # Gets name of the class.
25
+ def name
26
+ get_mandatory_attribute :name
27
+ end
28
+
29
+ ###
30
+ # Gets properties of the class.
31
+ # Returns nil for a class without properties.
32
+ def properties
33
+ self['properties']
34
+ end
35
+
36
+ ###
37
+ # Gets a property with the given name.
38
+ def property(name)
39
+ raise ArgumentError, 'no properties defined on class' if properties.nil?
40
+ props = properties.select { |i| i['name'] == name.to_s }
41
+ raise ::ArgumentError, "unknown property, name=#{name}" if props.empty?
42
+ raise ::ArgumentError, "too many properties found, name=#{name}" if props.size > 1 # just to be sure
43
+ props[0]
44
+ end
45
+
46
+ ###
47
+ # Gets the super-class.
48
+ def super_class
49
+ get_mandatory_attribute :superClass
50
+ end
51
+
52
+ ###
53
+ # Gets clusters of the class.
54
+ def clusters
55
+ get_mandatory_attribute :clusters
56
+ end
57
+
58
+ ###
59
+ # Gets the default cluster.
60
+ def default_cluster
61
+ get_mandatory_attribute :defaultCluster
62
+ end
63
+
64
+ ###
65
+ # Get flag whether the class is abstract.
66
+ def abstract?
67
+ get_mandatory_attribute :abstract
68
+ end
69
+
70
+ end
71
+
72
+
73
+ ###
74
+ # This module represents API to OrientDB's property.
75
+ module Property
76
+
77
+ ###
78
+ # Gets name of the property.
79
+ def name
80
+ get_mandatory_attribute :name
81
+ end
82
+
83
+ ###
84
+ # Gets type of the property.
85
+ def type
86
+ get_mandatory_attribute :type
87
+ end
88
+
89
+ ###
90
+ # Gets the 'mandatory' flag.
91
+ def mandatory
92
+ get_mandatory_attribute :mandatory
93
+ end
94
+
95
+ ###
96
+ # Gets the 'notNull' flag.
97
+ def not_null
98
+ get_mandatory_attribute :notNull
99
+ end
100
+
101
+ ###
102
+ # Gets the 'read-only' flag.
103
+ def read_only
104
+ get_mandatory_attribute :readonly
105
+ end
106
+
107
+ ###
108
+ # Gets linked class if the property is a link.
109
+ def linked_class
110
+ self['linkedClass']
111
+ end
112
+
113
+ ###
114
+ # Gets the minimal allowed value.
115
+ def min
116
+ self['min']
117
+ end
118
+
119
+ ###
120
+ # Gets the maximal allowed value.
121
+ def max
122
+ self['max']
123
+ end
124
+
125
+ end
126
+
127
+
128
+ ###
129
+ # This module represents API to document metadata.
130
+ module DocumentMetadata
131
+
132
+ ###
133
+ # Gets the document class.
134
+ def doc_class
135
+ self['@class']
136
+ end
137
+
138
+ ###
139
+ # Gets the document ID.
140
+ def doc_rid
141
+ @mixedin_rid = Rid.new(self['@rid']) if @mixedin_rid.nil?
142
+ @mixedin_rid
143
+ end
144
+
145
+ ###
146
+ # Gets the document version.
147
+ def doc_version
148
+ self['@version']
149
+ end
150
+
151
+ ###
152
+ # Gets the document type.
153
+ def doc_type
154
+ self['@type']
155
+ end
156
+
157
+ end
158
+
159
+ end
@@ -0,0 +1,43 @@
1
+ module Orientdb4r
2
+
3
+ ###
4
+ # This class represents a single sever/node in the Distributed Multi-Master Architecture
5
+ # accessible view REST API.
6
+ class RestNode < Node
7
+
8
+ # Name of cookie that represents a session.
9
+ SESSION_COOKIE_NAME = 'OSESSIONID'
10
+
11
+ attr_reader :ssl
12
+ # HTTP header 'User-Agent'
13
+ attr_accessor :user_agent
14
+
15
+ ###
16
+ # Constructor.
17
+ def initialize(host, port, ssl)
18
+ super(host, port)
19
+ raise ArgumentError, 'ssl flag cannot be blank' if blank?(ssl)
20
+ @ssl = ssl
21
+ end
22
+
23
+
24
+ def url #:nodoc:
25
+ "http#{'s' if ssl}://#{host}:#{port}"
26
+ end
27
+
28
+
29
+ # ----------------------------------------------------------- RestNode Stuff
30
+
31
+
32
+ ###
33
+ # Sends a HTTP request to the remote server.
34
+ # Use following if possible:
35
+ # * session_id
36
+ # * Keep-Alive (if possible)
37
+ def request(options)
38
+ raise NotImplementedError, 'this should be overridden by subclass'
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,77 @@
1
+ require 'rest_client'
2
+
3
+ module Orientdb4r
4
+
5
+ ###
6
+ # This class represents a single sever/node in the Distributed Multi-Master Architecture
7
+ # accessible via REST API and 'rest-client' library on the client side.
8
+ class RestClientNode < RestNode
9
+
10
+ def request(options) #:nodoc:
11
+ verify_options(options, {:user => :mandatory, :password => :mandatory, \
12
+ :uri => :mandatory, :method => :mandatory, :content_type => :optional, :data => :optional,
13
+ :no_session => :optional})
14
+
15
+ opts = options.clone # if not cloned we change original hash map that cannot be used more with load balancing
16
+
17
+ # URL
18
+ opts[:url] = "#{url}/#{opts[:uri]}"
19
+ opts.delete :uri
20
+
21
+ if content_type = opts.delete(:content_type)
22
+ opts[:headers] ||= {}
23
+ opts[:headers][:content_type] = content_type
24
+ end
25
+
26
+ # data
27
+ data = opts.delete :data
28
+ data = '' if data.nil? and :post == opts[:method] # POST has to have data
29
+ opts[:payload] = data unless data.nil?
30
+
31
+ # cookies
32
+ use_session = !opts.delete(:no_session)
33
+ opts[:cookies] = { SESSION_COOKIE_NAME => session_id} if use_session and !session_id.nil?
34
+ # headers
35
+ opts[:headers] = { 'User-Agent' => user_agent } unless user_agent.nil?
36
+
37
+ begin
38
+ response = ::RestClient::Request.new(opts).execute
39
+
40
+ # store session ID if received to reuse in next request
41
+ sessid = response.cookies[SESSION_COOKIE_NAME]
42
+ if sessid and session_id != sessid and use_session
43
+ @session_id = sessid
44
+ Orientdb4r::logger.debug "new session id: #{session_id}"
45
+ end
46
+
47
+ rescue Errno::ECONNREFUSED
48
+ raise NodeError
49
+ rescue ::RestClient::ServerBrokeConnection
50
+ raise NodeError
51
+ rescue ::RestClient::Exception => e
52
+ response = transform_error2_response(e)
53
+ end
54
+
55
+ response
56
+ end
57
+
58
+
59
+ private
60
+
61
+ ###
62
+ # Fakes an error thrown by the library into a response object with methods
63
+ # 'code' and 'body'.
64
+ def transform_error2_response(error)
65
+ response = ["#{error.message}: #{error.http_body}", error.http_code]
66
+ def response.body
67
+ self[0]
68
+ end
69
+ def response.code
70
+ self[1]
71
+ end
72
+ response
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,54 @@
1
+ module Orientdb4r
2
+
3
+ ###
4
+ # This class represents encapsulation of RecordID.
5
+ class Rid
6
+ include Utils
7
+
8
+ # Format validation regexp.
9
+ RID_REGEXP_PATTERN = /^#?\d+:\d+$/
10
+
11
+ attr_reader :cluster_id, :document_id
12
+
13
+ ###
14
+ # Constructor.
15
+ def initialize(rid)
16
+ raise ArgumentError, 'RID cannot be blank' if blank? rid
17
+ raise ArgumentError, 'RID is not String' unless rid.is_a? String
18
+ raise ArgumentError, "bad RID format, rid=#{rid}" unless rid =~ RID_REGEXP_PATTERN
19
+
20
+ rid = rid[1..-1] if rid.start_with? '#'
21
+ ids = rid.split ':'
22
+ self.cluster_id = ids[0].to_i
23
+ self.document_id = ids[1].to_i
24
+ end
25
+
26
+ ###
27
+ # Setter fo cluster ID.
28
+ def cluster_id=(cid)
29
+ @cluster_id = cid.to_i
30
+ end
31
+
32
+ ###
33
+ # Setter fo document ID.
34
+ def document_id=(did)
35
+ @document_id = did.to_i
36
+ end
37
+
38
+ def to_s #:nodoc:
39
+ "##{cluster_id}:#{document_id}"
40
+ end
41
+
42
+ ###
43
+ # Gets RID's string representation with no prefix.
44
+ def unprefixed
45
+ "#{cluster_id}:#{document_id}"
46
+ end
47
+
48
+ def ==(another_rid) #:nodoc:
49
+ self.cluster_id == another_rid.cluster_id and self.document_id == another_rid.document_id
50
+ end
51
+
52
+ end
53
+
54
+ end