vagas-orientdb4r 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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