qb 0.3.25 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/ansible.cfg +10 -1
  4. data/exe/.qb_interop_receive +3 -10
  5. data/exe/qb +8 -2
  6. data/lib/python/qb/__init__.py +6 -0
  7. data/{roles/qb/ruby/rspec/setup/tasks/persistence.yml → lib/python/qb/ansible/__init__.py} +0 -0
  8. data/lib/python/qb/ansible/modules/__init__.py +0 -0
  9. data/lib/python/qb/ansible/modules/docker/__init__.py +0 -0
  10. data/lib/python/qb/ansible/modules/docker/client.py +177 -0
  11. data/lib/python/qb/ansible/modules/docker/image_manager.py +754 -0
  12. data/lib/python/qb/ipc/__init__.py +0 -0
  13. data/lib/python/qb/ipc/stdio/__init__.py +99 -0
  14. data/lib/python/qb/ipc/stdio/logging.py +151 -0
  15. data/lib/qb.rb +3 -3
  16. data/lib/qb/ansible/cmds/playbook.rb +5 -14
  17. data/lib/qb/ansible/env.rb +36 -6
  18. data/lib/qb/ansible/module.rb +396 -152
  19. data/lib/qb/ansible/module/response.rb +195 -0
  20. data/lib/qb/ansible/modules.rb +42 -0
  21. data/lib/qb/ansible/modules/docker/image.rb +273 -0
  22. data/lib/qb/cli.rb +5 -18
  23. data/lib/qb/cli/run.rb +2 -2
  24. data/lib/qb/data.rb +22 -0
  25. data/lib/qb/data/immutable.rb +39 -0
  26. data/lib/qb/docker.rb +2 -0
  27. data/lib/qb/docker/cli.rb +430 -0
  28. data/lib/qb/docker/image.rb +207 -0
  29. data/lib/qb/docker/image/name.rb +309 -0
  30. data/lib/qb/docker/image/tag.rb +113 -0
  31. data/lib/qb/docker/repo.rb +0 -0
  32. data/lib/qb/errors.rb +17 -3
  33. data/lib/qb/execution.rb +83 -0
  34. data/lib/qb/ipc.rb +48 -0
  35. data/lib/qb/ipc/stdio.rb +32 -0
  36. data/lib/qb/ipc/stdio/client.rb +267 -0
  37. data/lib/qb/ipc/stdio/server.rb +229 -0
  38. data/lib/qb/ipc/stdio/server/in_service.rb +18 -0
  39. data/lib/qb/ipc/stdio/server/log_service.rb +168 -0
  40. data/lib/qb/ipc/stdio/server/out_service.rb +20 -0
  41. data/lib/qb/ipc/stdio/server/service.rb +229 -0
  42. data/lib/qb/options.rb +360 -502
  43. data/lib/qb/options/option.rb +293 -115
  44. data/lib/qb/options/option/option_parser_concern.rb +228 -0
  45. data/lib/qb/options/types.rb +73 -0
  46. data/lib/qb/package.rb +0 -1
  47. data/lib/qb/package/version.rb +179 -58
  48. data/lib/qb/package/version/from.rb +192 -51
  49. data/lib/qb/package/version/leveled.rb +1 -1
  50. data/lib/qb/path.rb +3 -2
  51. data/lib/qb/repo/git.rb +9 -85
  52. data/lib/qb/role/default_dir.rb +2 -2
  53. data/lib/qb/role/errors.rb +2 -8
  54. data/lib/qb/util.rb +1 -2
  55. data/lib/qb/util/bundler.rb +73 -43
  56. data/lib/qb/util/decorators.rb +99 -0
  57. data/lib/qb/util/interop.rb +7 -8
  58. data/lib/qb/util/resource.rb +12 -13
  59. data/lib/qb/version.rb +10 -0
  60. data/library/path_facts +5 -10
  61. data/library/qb.module.rb +105 -0
  62. data/library/stream +6 -26
  63. data/load/ansible/module/autorun.rb +25 -0
  64. data/load/ansible/module/script.rb +123 -0
  65. data/load/rebundle.rb +39 -0
  66. data/plugins/filter/dict_filters.py +56 -0
  67. data/plugins/{filter_plugins/path_plugins.py → filter/path_filters.py} +0 -0
  68. data/plugins/{filter_plugins/ruby_interop_plugins.py → filter/ruby_interop_filters.py} +1 -17
  69. data/plugins/{filter_plugins/string_plugins.py → filter/string_filters.py} +1 -20
  70. data/plugins/{filter_plugins/version_plugins.py → filter/version_filters.py} +3 -18
  71. data/plugins/{lookup_plugins/every.py → lookup/every_lookups.py} +0 -0
  72. data/plugins/{lookup_plugins/resolve.py → lookup/resolve_lookups.py} +0 -0
  73. data/plugins/{lookup_plugins/version.py → lookup/version_lookups.py} +0 -16
  74. data/plugins/test/dict_tests.py +36 -0
  75. data/plugins/test/string_tests.py +36 -0
  76. data/qb.gemspec +7 -3
  77. data/roles/nrser.rb/library/set_fact_with_ruby.rb +3 -9
  78. data/roles/nrser.state_mate/library/state +3 -17
  79. data/roles/qb/call/meta/qb.yml +1 -1
  80. data/roles/qb/dev/ref/repo/git/meta/qb.yml +1 -1
  81. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/defaults/main.yml +1 -1
  82. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/meta/main.yml +3 -2
  83. data/roles/qb/{ruby/rspec/setup → docker/mac/kubernetes}/meta/qb.yml +12 -7
  84. data/roles/qb/docker/mac/kubernetes/tasks/main.yml +45 -0
  85. data/roles/qb/git/check/clean/meta/qb.yml +1 -1
  86. data/roles/qb/git/ignore/meta/qb +10 -3
  87. data/roles/qb/git/submodule/update/library/git_submodule_update +17 -27
  88. data/roles/qb/github/pages/setup/meta/qb.yml +1 -1
  89. data/roles/qb/labs/atom/apm/meta/qb.yml +1 -1
  90. data/roles/qb/osx/git/change_case/meta/qb.yml +1 -1
  91. data/roles/qb/osx/notif/meta/qb.yml +1 -1
  92. data/roles/qb/pkg/bump/library/bump +4 -16
  93. data/roles/qb/role/qb/defaults/main.yml +2 -0
  94. data/roles/qb/role/qb/meta/qb.yml +10 -5
  95. data/roles/qb/role/qb/templates/qb.yml.j2 +7 -2
  96. data/roles/qb/role/templates/library/module.rb.j2 +12 -23
  97. data/roles/qb/role/templates/meta/main.yml.j2 +14 -1
  98. data/roles/qb/ruby/bundler/meta/qb.yml +1 -1
  99. data/roles/qb/ruby/dependency/meta/qb.yml +1 -1
  100. data/roles/qb/ruby/gem/bin_stubs/meta/qb.yml +1 -1
  101. data/roles/qb/ruby/gem/bin_stubs/templates/console +8 -2
  102. data/roles/qb/ruby/gem/build/meta/qb.yml +1 -1
  103. data/roles/qb/ruby/gem/new/meta/qb.yml +1 -1
  104. data/roles/qb/ruby/nrser/rspex/generate/meta/qb.yml +5 -5
  105. data/roles/qb/ruby/nrser/rspex/issue/meta/qb.yml +1 -1
  106. data/roles/qb/ruby/yard/clean/meta/qb.yml +1 -1
  107. data/roles/qb/ruby/yard/config/library/yard.get_output_dir +5 -15
  108. data/roles/qb/ruby/yard/config/meta/qb.yml +1 -1
  109. data/roles/qb/ruby/yard/setup/meta/qb.yml +1 -1
  110. metadata +71 -22
  111. data/lib/qb/ansible_module.rb +0 -5
  112. data/lib/qb/util/stdio.rb +0 -187
  113. data/roles/qb/ruby/rspec/setup/tasks/main.yml +0 -4
@@ -76,7 +76,7 @@ class QB::Package::Version::Leveled < QB::Package::Version
76
76
  # If the values do have a level.
77
77
  #
78
78
  def self.level_for prerelease: [], build: [], **etc
79
- return RELEASE if prerelease.empty? && build.empty?
79
+ return RELEASE if prerelease.empty?
80
80
 
81
81
  return DEV if prerelease[0] == DEV
82
82
 
@@ -8,6 +8,8 @@ require 'pathname'
8
8
  # Deps
9
9
  # -----------------------------------------------------------------------
10
10
  require 'nrser'
11
+ require 'nrser/refinements/types'
12
+ require 'nrser/props/immutable/instance_variables'
11
13
 
12
14
  # Package
13
15
  # -----------------------------------------------------------------------
@@ -17,7 +19,6 @@ require 'qb/repo/git'
17
19
  # Refinements
18
20
  # =======================================================================
19
21
 
20
- using NRSER
21
22
  using NRSER::Types
22
23
 
23
24
 
@@ -32,7 +33,7 @@ class QB::Path < Pathname
32
33
  # Mixins
33
34
  # =====================================================================
34
35
 
35
- include NRSER::Meta::Props
36
+ include NRSER::Props::Immutable::InstanceVariables
36
37
 
37
38
 
38
39
  # Props
@@ -17,17 +17,9 @@ require 'qb/util/resource'
17
17
  # Refinements
18
18
  # =======================================================================
19
19
 
20
- using NRSER
21
20
  using NRSER::Types
22
21
 
23
22
 
24
- # Declarations
25
- # =======================================================================
26
-
27
- module QB; end
28
- class QB::Repo < QB::Util::Resource; end
29
-
30
-
31
23
  # Definitions
32
24
  # =======================================================================
33
25
 
@@ -36,6 +28,8 @@ class QB::Repo < QB::Util::Resource; end
36
28
  #
37
29
  # The main entry point is {QB::Repo::Git.from_path}, which creates a
38
30
  #
31
+ module QB
32
+ class QB::Repo < QB::Util::Resource
39
33
  class QB::Repo::Git < QB::Repo
40
34
  autoload :User, 'qb/repo/git/user'
41
35
  autoload :GitHub, 'qb/repo/git/github'
@@ -60,78 +54,6 @@ class QB::Repo::Git < QB::Repo
60
54
  prop :is_clean, type: t.bool, source: :clean?
61
55
 
62
56
 
63
-
64
-
65
-
66
- # class GitHubRemote < NRSER::Meta::Props::Base
67
- # SSH_URL_RE = /^git@github\.com\:(?<owner>.*)\/(?<name>.*)\.git$/
68
- # HTTPS_URL_RE = /^https:\/\/github\.com\/(?<owner>.*)\/(?<name>.*)\.git$/
69
- #
70
- # prop :owner, type: t.str
71
- # prop :name, type: t.str
72
- # prop :api_response, type: t.maybe(t.hash)
73
- #
74
- # prop :full_name, type: t.str, source: :full_name
75
- #
76
- #
77
- # # Class Methods
78
- # # =====================================================================
79
- #
80
- # # Test if a Git SSH or HTTPS remote url points to GitHub.
81
- # #
82
- # # @param [String] url
83
- # #
84
- # # @return [Boolean]
85
- # #
86
- # def self.url? url
87
- # SSH_URL_RE.match(url) || HTTPS_URL_RE.match(url)
88
- # end # .url?
89
- #
90
- #
91
- # # Instantiate an instance from a Git SSH or HTTPS remote url that points
92
- # # to GitHub.
93
- # #
94
- # # @param [type] arg_name
95
- # # @todo Add name param description.
96
- # #
97
- # # @return [QB::Repo::Git::GitHubRemote]
98
- # # @todo Document return value.
99
- # #
100
- # def self.from_url url, use_api: false
101
- # match = SSH_URL_RE.match(git.origin) ||
102
- # HTTPS_URL_RE.match(git.origin)
103
- #
104
- # unless match
105
- # raise ArgumentError.new NRSER.squish <<-END
106
- # url #{ url.inspect } does not match GitHub SSH or HTTPS patterns.
107
- # END
108
- # end
109
- #
110
- # owner = match['owner']
111
- # name = match['name']
112
- #
113
- # if use_api
114
- #
115
- # end
116
- # end # .from_url
117
- #
118
- #
119
- #
120
- # # @todo Document full_name method.
121
- # #
122
- # # @param [type] arg_name
123
- # # @todo Add name param description.
124
- # #
125
- # # @return [return_type]
126
- # # @todo Document return value.
127
- # #
128
- # def full_name arg_name
129
- # "#{ owner }/#{ name }"
130
- # end # #full_name
131
- #
132
- # end
133
-
134
-
135
57
  # Class Methods
136
58
  # =====================================================================
137
59
 
@@ -194,7 +116,7 @@ class QB::Repo::Git < QB::Repo
194
116
 
195
117
  root_path = Pathname.new root_result.out.chomp
196
118
 
197
- user = User.new **NRSER.map_values(User.props.keys) {|key, _|
119
+ user = User.new **User.props.keys.assoc_to { |key|
198
120
  begin
199
121
  Cmds.chomp! "git config user.#{ key }"
200
122
  rescue
@@ -340,8 +262,10 @@ class QB::Repo::Git < QB::Repo
340
262
  end
341
263
 
342
264
 
343
- def api
344
- @api ||= ::Git.open root_path
265
+ def lib
266
+ lazy_var :@lib do
267
+ ::Git.open root_path
268
+ end
345
269
  end
346
270
 
347
271
 
@@ -374,7 +298,7 @@ class QB::Repo::Git < QB::Repo
374
298
 
375
299
 
376
300
  def tags
377
- api.tags.map &:name
301
+ lib.tags.map &:name
378
302
  end
379
303
 
380
- end # class QB::Repo::Git
304
+ end; end; end # class QB::Repo::Git
@@ -70,8 +70,8 @@ class QB::Role
70
70
  #
71
71
  def default_dir cwd, options
72
72
  logger.debug "CALLING default_dir",
73
- role: self.instance_variables.map_values { |k, v|
74
- self.instance_variable_get k
73
+ role: self.instance_variables.assoc_to { |key|
74
+ self.instance_variable_get key
75
75
  },
76
76
  cwd: cwd,
77
77
  options: options
@@ -1,9 +1,3 @@
1
- # Refinements
2
- # =======================================================================
3
-
4
- require 'nrser/refinements'
5
- using NRSER
6
-
7
1
 
8
2
  # Definitions
9
3
  # =======================================================================
@@ -40,8 +34,8 @@ module QB
40
34
  end
41
35
  end
42
36
 
43
- # raised when there's bad metadata
37
+ # raised when there's bad metadata
44
38
  class MetadataError < QB::StateError
45
39
  end
46
40
  end # Role
47
- end # QB
41
+ end # QB
@@ -1,9 +1,8 @@
1
- require_relative './util/stdio'
2
1
  require_relative './util/interop'
3
2
  require_relative './util/bundler'
3
+ require_relative './util/decorators'
4
4
 
5
5
  require 'nrser'
6
- using NRSER
7
6
 
8
7
  module QB
9
8
  module Util
@@ -1,56 +1,86 @@
1
1
  module QB; end
2
2
  module QB::Util; end
3
3
 
4
- module QB::Util::Bundler
5
- # needed for to clean the env if using bundler (like in dev).
4
+ module QB::Util::Bundler
5
+
6
+ # Are we running inside `bundler exec`?
6
7
  #
7
- # this is because the ansible gem module doesn't work right with the bundler
8
- # env vars set, so we need to remove them, but when running in dev we want
9
- # modules written in ruby like nrser.state_mate's `state` script to have
10
- # access to them so it can fire up bundler and get the right libraries.
8
+ # @return [Boolean]
11
9
  #
12
- # to accomplish this, we detect Bundler, and when it's present we copy the
13
- # bundler-related env vars (which i found by looking at
14
- # https://github.com/bundler/bundler/blob/master/lib/bundler.rb#L257)
15
- # into a hash to pass around the env sanitization, then copy them into
16
- # corresponding 'QB_DEV_ENV_<NAME>' vars that modules can restore.
10
+ def self.bundled?
11
+ defined? ::Bundler
12
+ end # .bundled?
13
+
14
+
15
+ # Wrapper around {Bundler.with_clean_env} that copies the Bundler ENV vars
16
+ # to other keys, allowing that Bundler ENV to be re-instated later,
17
+ # specifically by child processes like Ansible module scripts that inherit
18
+ # the ENV.
17
19
  #
18
- # we also set a 'QB_DEV_ENV=true' env var for modules to easily detect that
19
- # we're running in dev and restore the variables.
20
+ # We execute Ansible commands in this context because any Ruby processes it
21
+ # starts want the system environment, not the Bundler environment that QB
22
+ # may be running in. In particular, Ansible's `gem` module fails if the
23
+ # Bundler ENV vars are still in place, which totally make sense.
24
+ #
25
+ # Instead, we let programs that want to boot up the possibly separate
26
+ # environment that QB is running in (so they can require it - again,
27
+ # Ansible module scripts, specifically those using {QB::Ansible::Module})
28
+ # can load `//load/rebundle.rb`, which will restore the Bundler ENV vars
29
+ # (and `require 'bundler/setup'`).
30
+ #
31
+ # We make the absolute path to `//load/rebundle.rb` available in the "clean"
32
+ # ENV as the `QB_REBUNDLE_PATH` var, so child Ruby programs can drop a
33
+ # single line at the top of the file:
34
+ #
35
+ # load ENV['QB_REBUNDLE_PATH'] if ENV['QB_REBUNDLE_PATH']
36
+ #
37
+ # and be set up to require QB files.
38
+ #
39
+ # If QB is *not* running in Bundler ({#bundled?} returns `false`) then
40
+ # this method simply calls `&block` and returns the value.
41
+ #
42
+ # @param [Proc<() => RESULT>] &block
43
+ # Block to execute in the "clean" env.
44
+ #
45
+ # @return [RESULT]
46
+ # Whatever `&block` returns when called.
20
47
  #
21
48
  def self.with_clean_env &block
22
- if defined? ::Bundler
23
- # copy the Bundler env vars into a hash
24
- dev_env = ENV.select {|k, v|
25
- k.start_with?("BUNDLE_") ||
26
- [
27
- 'GEM_HOME',
28
- 'GEM_PATH',
29
- 'MANPATH',
30
- 'RUBYOPT',
31
- 'RUBYLIB',
32
- ].include?(k)
33
- }
49
+ # If we're not running "bundled" then just call the block and return
50
+ return block.call unless bundled?
51
+
52
+ # copy the Bundler env vars into a hash
53
+ dev_env = ENV.select {|k, v|
54
+ k.start_with?("BUNDLE_") ||
55
+ [
56
+ 'GEM_HOME',
57
+ 'GEM_PATH',
58
+ 'MANPATH',
59
+ 'RUBYOPT',
60
+ 'RUBYLIB',
61
+ ].include?(k)
62
+ }
63
+
64
+ qb_env = ENV.select { |k, v| k.start_with? 'QB_' }
65
+
66
+ ::Bundler.with_clean_env do
67
+ # Now that we're in a clean env, copy the Bundler env vars into
68
+ # 'QB_BUNDLER_ENV_<NAME>' vars.
69
+ dev_env.each { |k, v| ENV["QB_BUNDLER_ENV_#{ k }"] = v }
70
+
71
+ # Set the path to the `//load/rebundle.rb` script in an ENV var.
72
+ #
73
+ # Child Ruby processes that want to load up the environment QB was run
74
+ # in look for this and load it if they find it, restoring the Bundler /
75
+ # Ruby Gems ENV vars, allowing them to `require 'qb'`, etc.
76
+ #
77
+ ENV['QB_REBUNDLE_PATH'] = (QB::ROOT / 'load' / 'rebundle.rb').to_s
34
78
 
35
- qb_env = ENV.select {|k, v| k.start_with? 'QB_'}
79
+ qb_env.each { |k, v| ENV[k] = v }
36
80
 
37
- ::Bundler.with_clean_env do
38
- # now that we're in a clean env, copy the Bundler env vars into
39
- # 'QB_DEV_ENV_<NAME>' vars.
40
- dev_env.each {|k, v| ENV["QB_DEV_ENV_#{ k }"] = v}
41
-
42
- # set the flag that will be used by modules to know to restore the
43
- # variables
44
- ENV['QB_DEV_ENV'] = 'true'
45
-
46
- qb_env.each {|k, v| ENV[k] = v}
47
-
48
- # invoke the block
49
- block.call
50
- end
51
- else
52
- # bundler isn't loaded, so no env var silliness to deal with
81
+ # invoke the block
53
82
  block.call
54
- end
83
+ end # ::Bundler.with_clean_env
55
84
  end # .with_clean_env
85
+
56
86
  end # module QB::Util::Bundler
@@ -0,0 +1,99 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ require 'method_decorators'
11
+
12
+
13
+ # Definitions
14
+ # =======================================================================
15
+
16
+ # Python-style decorators using the `method_decorators` gem.
17
+ #
18
+ # @see https://rubygems.org/gems/method_decorators
19
+ #
20
+ # @example Usage
21
+ # class A
22
+ # extend MethodDecorators
23
+ #
24
+ # +QB::Util::Decorators::EnumFor
25
+ # def get_stuff
26
+ # yield 1
27
+ # yield 2
28
+ # yield 3
29
+ # end
30
+ #
31
+ # end
32
+ #
33
+ # A.new.get_stuff.class
34
+ # # => Enumerator
35
+ #
36
+ module QB; module Util; module Decorators
37
+
38
+ # Wrap a method that yields, returning an {Enumerator} if no block is
39
+ # given.
40
+ #
41
+ # Implements the common "enum_for" pattern:
42
+ #
43
+ # def f
44
+ # return enum_for( __method__ ) unless block_given?
45
+ # yield 1
46
+ # # ...
47
+ # end
48
+ #
49
+ class EnumFor < MethodDecorators::Decorator
50
+ # @param [Method] target
51
+ # The decorated method, already bound to the receiver.
52
+ #
53
+ # The `method_decorators` gem calls this `orig`, but I thought `target`
54
+ # made more sense.
55
+ #
56
+ # @param [*] receiver
57
+ # The object that will receive the call to `target`.
58
+ #
59
+ # The `method_decorators` gem calls this `this`, but I thought `receiver`
60
+ # made more sense.
61
+ #
62
+ # It's just `target.receiver`, but the API is how it is.
63
+ #
64
+ # @param [Array] *args
65
+ # Any arguments the decorated method was called with.
66
+ #
67
+ # @param [Proc?] &block
68
+ # The block the decorated method was called with (if any).
69
+ #
70
+ def call target, receiver, *args, &block
71
+ if block
72
+ target.call *args, &block
73
+ else
74
+ receiver.enum_for target.name, *args
75
+ end
76
+ end
77
+ end # EnumFor
78
+
79
+
80
+ # Don't all {NRSER::Props} arguments in last position to become keyword
81
+ # options.
82
+ #
83
+ # Since {NRSER::Props} objects can be subclasses of {Hash}, etc., they can
84
+ # erroneously end up being considered keyword options.
85
+ #
86
+ # This decorator prevents that. If you want to pass an {NRSER::Props} as the
87
+ # keywords, calling `#to_h` or something on it first should work.
88
+ #
89
+ class NoPropsInKwds < MethodDecorators::Decorator
90
+ def call target, receiver, *args, &block
91
+ if args.last.is_a? NRSER::Props
92
+ target.call *args, {}, &block
93
+ else
94
+ target.call *args, &block
95
+ end
96
+ end
97
+ end # class NoPropsInKwds
98
+
99
+ end; end; end # module QB::Util::Decorators
@@ -1,12 +1,11 @@
1
- require 'nrser/refinements'
1
+ require 'nrser'
2
+ require 'nrser/props'
2
3
 
3
- using NRSER
4
-
5
- module QB
6
- module Util
4
+ module QB
5
+ module Util
7
6
 
8
7
  module Interop
9
- include SemanticLogger::Loggable
8
+ include NRSER::Log::Mixin
10
9
 
11
10
  class << self
12
11
 
@@ -14,8 +13,8 @@ module Interop
14
13
  logger.debug "Starting #send_to_instance..."
15
14
 
16
15
  obj = if data.is_a?( Hash ) &&
17
- data.key?( NRSER::Meta::Props::DEFAULT_CLASS_KEY )
18
- NRSER::Meta::Props.UNSAFE_load_instance_from_data data
16
+ data.key?( NRSER::Props::DEFAULT_CLASS_KEY )
17
+ NRSER::Props.UNSAFE_load_instance_from_data data
19
18
  else
20
19
  data
21
20
  end