arango-driver 3.5.0.alpha0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +1073 -0
- data/arango_opal.js +15 -0
- data/lib/arango-driver.rb +61 -0
- data/lib/arango.rb +96 -0
- data/lib/arango/aql.rb +188 -0
- data/lib/arango/collection.rb +575 -0
- data/lib/arango/collection/documents.rb +122 -0
- data/lib/arango/collection/edges.rb +149 -0
- data/lib/arango/collection/importing.rb +57 -0
- data/lib/arango/collection/indexes.rb +53 -0
- data/lib/arango/collection/replication.rb +24 -0
- data/lib/arango/collection/user.rb +28 -0
- data/lib/arango/cursor.rb +67 -0
- data/lib/arango/database.rb +188 -0
- data/lib/arango/database/analyzer.rb +21 -0
- data/lib/arango/database/aql_functions.rb +54 -0
- data/lib/arango/database/aql_queries.rb +114 -0
- data/lib/arango/database/aql_query_cache.rb +27 -0
- data/lib/arango/database/collections.rb +100 -0
- data/lib/arango/database/foxx_services.rb +103 -0
- data/lib/arango/database/graph_access.rb +27 -0
- data/lib/arango/database/http_route.rb +9 -0
- data/lib/arango/database/replication.rb +96 -0
- data/lib/arango/database/stream_transactions.rb +25 -0
- data/lib/arango/database/tasks.rb +67 -0
- data/lib/arango/database/transactions.rb +15 -0
- data/lib/arango/database/user.rb +26 -0
- data/lib/arango/database/view_access.rb +37 -0
- data/lib/arango/document.rb +443 -0
- data/lib/arango/edge.rb +164 -0
- data/lib/arango/error.rb +97 -0
- data/lib/arango/error_db.rb +27 -0
- data/lib/arango/foxx.rb +255 -0
- data/lib/arango/graph.rb +202 -0
- data/lib/arango/graph/basics.rb +39 -0
- data/lib/arango/graph/edge_access.rb +56 -0
- data/lib/arango/graph/vertex_access.rb +33 -0
- data/lib/arango/helper/collection_assignment.rb +13 -0
- data/lib/arango/helper/database_assignment.rb +14 -0
- data/lib/arango/helper/request_method.rb +45 -0
- data/lib/arango/helper/return.rb +21 -0
- data/lib/arango/helper/satisfaction.rb +28 -0
- data/lib/arango/helper/server_assignment.rb +13 -0
- data/lib/arango/helper/traversal.rb +12 -0
- data/lib/arango/index.rb +103 -0
- data/lib/arango/replication.rb +231 -0
- data/lib/arango/request.rb +92 -0
- data/lib/arango/request_batch.rb +174 -0
- data/lib/arango/result.rb +130 -0
- data/lib/arango/search_view.rb +23 -0
- data/lib/arango/server.rb +68 -0
- data/lib/arango/server/administration.rb +296 -0
- data/lib/arango/server/agency.rb +23 -0
- data/lib/arango/server/async.rb +51 -0
- data/lib/arango/server/batch.rb +35 -0
- data/lib/arango/server/config.rb +76 -0
- data/lib/arango/server/databases.rb +71 -0
- data/lib/arango/server/monitoring.rb +17 -0
- data/lib/arango/server/opal_support.rb +95 -0
- data/lib/arango/server/tasks.rb +69 -0
- data/lib/arango/server/user.rb +22 -0
- data/lib/arango/task.rb +223 -0
- data/lib/arango/transaction.rb +113 -0
- data/lib/arango/traversal.rb +212 -0
- data/lib/arango/user.rb +174 -0
- data/lib/arango/version.rb +3 -0
- data/lib/arango/vertex.rb +112 -0
- data/lib/arango/view.rb +124 -0
- data/lib/arango/view/basics.rb +25 -0
- metadata +296 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module Agency
|
4
|
+
def agency_config
|
5
|
+
request("GET", "_api/agency/config")
|
6
|
+
end
|
7
|
+
|
8
|
+
def agency_write(body:, agency_mode: nil)
|
9
|
+
satisfy_category?(agency_mode, ["waitForCommmitted", "waitForSequenced", "noWait", nil])
|
10
|
+
headers = {"X-ArangoDB-Agency-Mode": agency_mode}
|
11
|
+
request("POST", "_api/agency/write", headers: headers,
|
12
|
+
body: body)
|
13
|
+
end
|
14
|
+
|
15
|
+
def agency_read(body:, agency_mode: nil)
|
16
|
+
satisfy_category?(agency_mode, ["waitForCommmitted", "waitForSequenced", "noWait", nil])
|
17
|
+
headers = {"X-ArangoDB-Agency-Mode": agency_mode}
|
18
|
+
request("POST", "_api/agency/read", headers: headers,
|
19
|
+
body: body)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module Async
|
4
|
+
# === ASYNC ===
|
5
|
+
|
6
|
+
def fetch_async(id:)
|
7
|
+
request("PUT", "_api/job/#{id}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def cancel_async(id:)
|
11
|
+
request("PUT", "_api/job/#{id}/cancel", key: :result)
|
12
|
+
end
|
13
|
+
|
14
|
+
def destroy_async(id:, stamp: nil)
|
15
|
+
query = {stamp: stamp}
|
16
|
+
request("DELETE", "_api/job/#{id}", query: query, key: :result)
|
17
|
+
end
|
18
|
+
|
19
|
+
def destroy_async_by_type(type:, stamp: nil)
|
20
|
+
satisfy_category?(type, %w[all expired])
|
21
|
+
query = {stamp: stamp}
|
22
|
+
request("DELETE", "_api/job/#{type}", query: query)
|
23
|
+
end
|
24
|
+
|
25
|
+
def destroy_all_async
|
26
|
+
destroy_async_by_type(type: "all")
|
27
|
+
end
|
28
|
+
|
29
|
+
def destroy_expired_async
|
30
|
+
destroy_async_by_type(type: "expired")
|
31
|
+
end
|
32
|
+
|
33
|
+
def retrieve_async(id:)
|
34
|
+
request("GET", "_api/job/#{id}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def retrieve_async_by_type(type:, count: nil)
|
38
|
+
satisfy_category?(type, %w[done pending])
|
39
|
+
request("GET", "_api/job/#{type}", query: {count: count})
|
40
|
+
end
|
41
|
+
|
42
|
+
def retrieve_done_async(count: nil)
|
43
|
+
retrieve_async_by_type(type: "done", count: count)
|
44
|
+
end
|
45
|
+
|
46
|
+
def retrieve_pending_async(count: nil)
|
47
|
+
retrieve_async_by_type(type: "pending", count: count)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module Batch
|
4
|
+
# === BATCH ===
|
5
|
+
|
6
|
+
def batch(requests: [])
|
7
|
+
Arango::RequestBatch.new(server: self, requests: requests)
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_dump_batch(ttl:, dbserver: nil)
|
11
|
+
query = { DBserver: dbserver }
|
12
|
+
body = { ttl: ttl }
|
13
|
+
result = request("POST", "_api/replication/batch",
|
14
|
+
body: body, query: query)
|
15
|
+
return result if return_directly?(result)
|
16
|
+
return result[:id]
|
17
|
+
end
|
18
|
+
|
19
|
+
def destroy_dump_batch(id:, dbserver: nil)
|
20
|
+
query = {DBserver: dbserver}
|
21
|
+
result = request("DELETE", "_api/replication/batch/#{id}", query: query)
|
22
|
+
return_delete(result)
|
23
|
+
end
|
24
|
+
|
25
|
+
def prolong_dump_batch(id:, ttl:, dbserver: nil)
|
26
|
+
query = { DBserver: dbserver }
|
27
|
+
body = { ttl: ttl }
|
28
|
+
result = request("PUT", "_api/replication/batch/#{id}",
|
29
|
+
body: body, query: query)
|
30
|
+
return result if return_directly?(result)
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module Config
|
4
|
+
attr_reader :active_cache, :async, :base_uri, :cache, :host, :password, :port, :return_output, :tls, :username
|
5
|
+
attr_accessor :size, :timeout, :verbose, :warning
|
6
|
+
|
7
|
+
def username=(username)
|
8
|
+
@username = username
|
9
|
+
@options[:basic_auth][:username] = @username
|
10
|
+
@request.options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def password=(password)
|
14
|
+
@password = password
|
15
|
+
@options[:basic_auth][:password] = @password
|
16
|
+
@request.options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
def host=(host)
|
20
|
+
@host = host
|
21
|
+
update_base_uri
|
22
|
+
end
|
23
|
+
|
24
|
+
def port=(port)
|
25
|
+
@port = port
|
26
|
+
update_base_uri
|
27
|
+
end
|
28
|
+
|
29
|
+
def tls=(tls)
|
30
|
+
satisfy_category?(tls, [false, true])
|
31
|
+
@tls = tls
|
32
|
+
update_base_uri
|
33
|
+
end
|
34
|
+
|
35
|
+
def active_cache=(active)
|
36
|
+
satisfy_category?(active, [true, false])
|
37
|
+
@active_cache = active
|
38
|
+
if @active_cache
|
39
|
+
@cache ||= Arango::Cache.new
|
40
|
+
elsif !@cache.nil?
|
41
|
+
@cache.clear
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def return_output=(return_output)
|
46
|
+
satisfy_category?(return_output, [true, false])
|
47
|
+
@return_output = return_output
|
48
|
+
@request.return_output = return_output
|
49
|
+
end
|
50
|
+
|
51
|
+
def verbose=(verbose)
|
52
|
+
satisfy_category?(verbose, [true, false])
|
53
|
+
@verbose = verbose
|
54
|
+
@request.verbose = verbose
|
55
|
+
end
|
56
|
+
|
57
|
+
def async=(async)
|
58
|
+
satisfy_category?(async, ["true", "false", false, true, "store", :store])
|
59
|
+
case async
|
60
|
+
when true, "true"
|
61
|
+
@options[:headers]["x-arango-async"] = "true"
|
62
|
+
@async = true
|
63
|
+
when :store, "store"
|
64
|
+
@options[:headers]["x-arango-async"] = "store"
|
65
|
+
@async = :store
|
66
|
+
when false, "false"
|
67
|
+
@options[:headers].delete("x-arango-async")
|
68
|
+
@async = false
|
69
|
+
end
|
70
|
+
@request.async = @async
|
71
|
+
@request.options = @options
|
72
|
+
end
|
73
|
+
alias assign_async async=
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module Databases
|
4
|
+
# Retrieves all databases.
|
5
|
+
# @return [Array<Arango::Database>]
|
6
|
+
def all_databases
|
7
|
+
Arango::Database.all(server: self)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Retrieves all databases the current user can access.
|
11
|
+
# @return [Array<Arango::Database>]
|
12
|
+
def all_user_databases
|
13
|
+
Arango::Database.all_user_databases(server: self)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Retrieves a list of all databases.
|
17
|
+
# @return [Array<String>] List of database names.
|
18
|
+
def list_databases
|
19
|
+
Arango::Database.list(server: self)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Retrieves a list of all databases the current user can access.
|
23
|
+
# @return [Array<String>] List of database names.
|
24
|
+
def list_user_databases
|
25
|
+
Arango::Database.list_user_databases(server: self)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a new database.
|
29
|
+
# @param name [String] The name of the database
|
30
|
+
# @return [Arango::Database] The instance of the database created.
|
31
|
+
def create_database(name)
|
32
|
+
Arango::Database.new(name, server: self).create
|
33
|
+
end
|
34
|
+
|
35
|
+
# Instantiates a new database, but does not store it on th server.
|
36
|
+
# @param name [String] The name of the database
|
37
|
+
# @return [Arango::Database] The instance of the database.
|
38
|
+
def new_database(name)
|
39
|
+
Arango::Database.new(name, server: self)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Removes a database.
|
43
|
+
# @param name [String] The name of the database
|
44
|
+
# @return nil
|
45
|
+
def drop_database(name)
|
46
|
+
Arango::Database.drop(name, server: self)
|
47
|
+
end
|
48
|
+
alias delete_database drop_database
|
49
|
+
alias destroy_database drop_database
|
50
|
+
|
51
|
+
# Get database from server.
|
52
|
+
# @param name [String] The name of the database
|
53
|
+
# @return [Arango::Database] The instance of the database.
|
54
|
+
def get_database(name)
|
55
|
+
db = Arango::Database.get(name, server: self)
|
56
|
+
Arango.current_database = db if Arango.current_server == self
|
57
|
+
db
|
58
|
+
end
|
59
|
+
alias fetch_database get_database
|
60
|
+
alias retrieve_database get_database
|
61
|
+
|
62
|
+
# Check if database exists.
|
63
|
+
# @param name [String] Name of the database.
|
64
|
+
# @return [Boolean]
|
65
|
+
def exist_database?(name)
|
66
|
+
Arango::Database.exist?(name, server: self)
|
67
|
+
end
|
68
|
+
alias database_exist? exist_database?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module Monitoring
|
4
|
+
# === MONITORING ===
|
5
|
+
|
6
|
+
def cluster_health
|
7
|
+
request("GET", "_admin/health")
|
8
|
+
end
|
9
|
+
|
10
|
+
def cluster_statistics dbserver:
|
11
|
+
query = {DBserver: dbserver}
|
12
|
+
request("GET", "_admin/clusterStatistics", query: query)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module OpalSupport
|
4
|
+
# From the Arango 2.8 documentation:
|
5
|
+
#
|
6
|
+
# Modules Path versus Modules Collection
|
7
|
+
#
|
8
|
+
# ArangoDB comes with predefined modules defined in the file-system under the path specified by
|
9
|
+
# startup.startup-directory. In a standard installation this point to the system share directory.
|
10
|
+
# Even if you are an administrator of ArangoDB you might not have write permissions to this location.
|
11
|
+
# On the other hand, in order to deploy some extension for ArangoDB, you might need to install additional
|
12
|
+
# JavaScript modules. This would require you to become root and copy the files into the share directory.
|
13
|
+
# In order to ease the deployment of extensions, ArangoDB uses a second mechanism to look up JavaScript modules.
|
14
|
+
#
|
15
|
+
# JavaScript modules can either be stored in the filesystem as regular file or
|
16
|
+
# in the database collection _modules.
|
17
|
+
#
|
18
|
+
# If you execute
|
19
|
+
#
|
20
|
+
# require("com/example/extension")
|
21
|
+
#
|
22
|
+
# then ArangoDB will try to locate the corresponding JavaScript as file as follows
|
23
|
+
#
|
24
|
+
# There is a cache for the results of previous require calls. First of all ArangoDB checks if
|
25
|
+
# com/example/extension is already in the modules cache. If it is, the export object for this module
|
26
|
+
# is returned. No further JavaScript is executed.
|
27
|
+
#
|
28
|
+
# ArangoDB will then check, if there is a file called com/example/extension.js in the system search path.
|
29
|
+
# If such a file exists, it is executed in a new module context and the value of exports object is returned.
|
30
|
+
# This value is also stored in the module cache.
|
31
|
+
#
|
32
|
+
# If no file can be found, ArangoDB will check if the collection _modules contains a document of the form
|
33
|
+
#
|
34
|
+
# {
|
35
|
+
# path: "/com/example/extension",
|
36
|
+
# content: "...."
|
37
|
+
# }
|
38
|
+
#
|
39
|
+
# Note: The leading / is important - even if you call require without a leading /. If such a document exists,
|
40
|
+
# then the value of the content attribute must contain the JavaScript code of the module. This string is
|
41
|
+
# executed in a new module context and the value of exports object is returned. This value is also stored
|
42
|
+
# in the module cache.
|
43
|
+
#
|
44
|
+
#
|
45
|
+
# Taken from the Changelog for 3.5.0.rc.1,
|
46
|
+
# https://github.com/arangodb/arangodb/blob/43fa37e35935e095dae31f250456f030da3c451f/CHANGELOG#L826:
|
47
|
+
#
|
48
|
+
# * Do not create `_modules` collection for new databases/installations.
|
49
|
+
#
|
50
|
+
# `_modules` is only needed for custom modules, and in case a custom
|
51
|
+
# module is defined via `defineModule`, the _modules collection will
|
52
|
+
# be created lazily automatically.
|
53
|
+
#
|
54
|
+
# Existing modules in existing `_modules` collections will remain
|
55
|
+
# functional even after this change
|
56
|
+
#
|
57
|
+
#
|
58
|
+
# So this is what we use for storing the opal module in arango and making it available.
|
59
|
+
|
60
|
+
def install_opal_module(database = '_system', force: false)
|
61
|
+
database = database.name unless database.class == String
|
62
|
+
dirname = File.dirname(__FILE__)
|
63
|
+
filename = File.expand_path(File.join(dirname, '..', '..', '..', 'arango_opal.js'))
|
64
|
+
content = File.read(filename)
|
65
|
+
system_db = get_database(database)
|
66
|
+
system_db.create_collection('_modules', is_system: true) unless system_db.collection_exist?('_modules', exclude_system: false)
|
67
|
+
modules_collection = system_db.get_collection('_modules')
|
68
|
+
opal_module_doc = modules_collection.get_document(path: '/opal')
|
69
|
+
if opal_module_doc
|
70
|
+
opal_module_doc.content = content
|
71
|
+
opal_module_doc.update
|
72
|
+
else
|
73
|
+
modules_collection.create_document({ path: '/opal', content: content })
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# def install_opal_parser_module(database = '_system', force: false)
|
78
|
+
# database = database.name unless database.class == String
|
79
|
+
# dirname = File.dirname(__FILE__)
|
80
|
+
# filename = File.expand_path(File.join(dirname, '..', '..', '..', 'arango_opal_parser.js'))
|
81
|
+
# content = File.read(filename)
|
82
|
+
# system_db = get_database(database)
|
83
|
+
# system_db.create_collection('_modules', is_system: true) unless system_db.collection_exist?('_modules', exclude_system: false)
|
84
|
+
# modules_collection = system_db.get_collection('_modules')
|
85
|
+
# opal_module_doc = modules_collection.get_document(path: '/opal-parser')
|
86
|
+
# if opal_module_doc
|
87
|
+
# opal_module_doc.content = content
|
88
|
+
# opal_module_doc.update
|
89
|
+
# else
|
90
|
+
# modules_collection.create_document({ path: '/opal-parser', content: content })
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module Tasks
|
4
|
+
|
5
|
+
# Get all tasks.
|
6
|
+
#
|
7
|
+
# @return [Array<Arango::Task>]
|
8
|
+
def all_tasks
|
9
|
+
Arango::Task.all(server: self)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a new task with given id, task is saved to the database.
|
13
|
+
# @param id [String]
|
14
|
+
# @param command [String] The javascript code to execute.
|
15
|
+
# @param name [String] The task name, optional.
|
16
|
+
# @param offset [Integer] The number of seconds initial delay, optional.
|
17
|
+
# @param params [Hash] Hash of params to pass to the command, optional.
|
18
|
+
# @param period [Integer] Number of seconds between executions, optional.
|
19
|
+
# @return [Arango::Task]
|
20
|
+
def create_task(id, command:, name: nil, offset: nil, params: nil, period: nil)
|
21
|
+
Arango::Task.new(id, command: command, name: name, offset: offset, params: params, period: period, server: self).create
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get a task from the server.
|
25
|
+
# @param id [String]
|
26
|
+
# @return [Arango::Task]
|
27
|
+
def get_task(id)
|
28
|
+
Arango::Task.get(id, server: self)
|
29
|
+
end
|
30
|
+
alias fetch_task get_task
|
31
|
+
alias retrieve_task get_task
|
32
|
+
|
33
|
+
# Instantiate a new task with given id, task is not saved to the database.
|
34
|
+
# @param id [String]
|
35
|
+
# @param command [String] The javascript code to execute, optional.
|
36
|
+
# @param name [String] The task name, optional.
|
37
|
+
# @param offset [Integer] The number of seconds initial delay, optional.
|
38
|
+
# @param params [Hash] Hash of params to pass to the command, optional.
|
39
|
+
# @param period [Integer] Number of seconds between executions, optional.
|
40
|
+
# @return [Arango::Task]
|
41
|
+
def new_task(id, command: nil, name: nil, offset: nil, params: nil, period: nil)
|
42
|
+
Arango::Task.new(id, command: command, name: name, offset: offset, params: params, period: period, server: self)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get a list of all task ids.
|
46
|
+
# @return [Array<String>]
|
47
|
+
def list_tasks
|
48
|
+
Arango::Task.list(server: self)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Delete task with given id.
|
52
|
+
# @param id [String]
|
53
|
+
# @return [Boolean] Returns true if task has been deleted.
|
54
|
+
def drop_task(id)
|
55
|
+
Arango::Task.delete(id, server: self)
|
56
|
+
end
|
57
|
+
alias delete_task drop_task
|
58
|
+
alias destroy_task drop_task
|
59
|
+
|
60
|
+
# Checks existence of a task.
|
61
|
+
# @param id [String]
|
62
|
+
# @return [Boolean] Returns true if the task exists, otherwise false.
|
63
|
+
def exist_task?(id)
|
64
|
+
Arango::Task.exist?(id, server: self)
|
65
|
+
end
|
66
|
+
alias task_exist? exist_task?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Arango
|
2
|
+
class Server
|
3
|
+
module User
|
4
|
+
# === USER ===
|
5
|
+
|
6
|
+
def user(password: "", name:, extra: {}, active: nil)
|
7
|
+
Arango::User.new(host: self, password: password, name: name, extra: extra,
|
8
|
+
active: active)
|
9
|
+
end
|
10
|
+
|
11
|
+
def users
|
12
|
+
result = request("GET", "_api/user", key: :result)
|
13
|
+
return result if return_directly?(result)
|
14
|
+
result.map do |user|
|
15
|
+
Arango::User.new(name: user[:user], active: user[:active],
|
16
|
+
extra: user[:extra], host: self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|