k_doc 0.0.16 → 0.0.22

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.
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDoc
4
+ # A container acts a base data object for any data that requires tagging
5
+ # such as unique key, type and namespace.
6
+ # Rename: BlockProcessor
7
+ module BlockProcessor
8
+ attr_accessor :block
9
+
10
+ def initialize_block(_opts, &block)
11
+ @block = block if block_given?
12
+ end
13
+
14
+ def execute_block(run_actions: nil)
15
+ eval_block
16
+ run_on_action if run_actions
17
+ end
18
+
19
+ def eval_block
20
+ return if @block.nil?
21
+
22
+ instance_eval(&@block)
23
+ rescue StandardError => e
24
+ log.error('Standard error in document')
25
+ # puts "key #{unique_key}"
26
+ # puts "file #{KUtil.data.console_file_hyperlink(resource.file, resource.file)}"
27
+ log.error(e.message)
28
+ @error = e
29
+ raise
30
+ end
31
+
32
+ def run_on_action
33
+ return if @block.nil?
34
+
35
+ on_action if respond_to?(:on_action)
36
+ rescue StandardError => e
37
+ log.error('Standard error while running actions')
38
+ # puts "key #{unique_key}"
39
+ # puts "file #{KUtil.data.console_file_hyperlink(resource.file, resource.file)}"
40
+ log.error(e.message)
41
+ @error = e
42
+ raise
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDoc
4
+ # Composite Design Pattern: https://refactoring.guru/design-patterns/composite
5
+ module ComposableComponents
6
+ # Parent allows upwards navigation to parent component
7
+ attr_reader :parent
8
+
9
+ # Components allow downwards navigation plus access to sub-components
10
+ attr_reader :components
11
+
12
+ def attach_parent(parent)
13
+ @parent = parent
14
+ end
15
+
16
+ def navigate_parent
17
+ parent.nil? ? self : parent
18
+ end
19
+
20
+ def root?
21
+ parent.nil?
22
+ end
23
+
24
+ # Implement as needed (Implement is not provided here because you may want to use hash or array and have additional logic)
25
+ # def reset_components
26
+ # end
27
+ # def add_component
28
+ # end
29
+ # def remove_component
30
+ # end
31
+ # def get_components
32
+ # end
33
+ # def has_component?
34
+ # end
35
+ # def execute
36
+ # end
37
+ end
38
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDoc
4
+ # Data acts as a base data object containers
5
+ module Datum
6
+ include KDoc::Guarded
7
+
8
+ attr_reader :data
9
+
10
+ def initialize_data(opts)
11
+ @default_data_type = opts.delete(:default_data_type) if opts.key?(:default_data_type)
12
+ @data = opts.delete(:data) || opts.delete(:default_data) || default_data_type.new
13
+
14
+ warn("Incompatible data type - #{default_data_type} is incompatible with #{data.class} in constructor") unless data.is_a?(default_data_type)
15
+ end
16
+
17
+ def default_data_type
18
+ raise 'Implement default_data_type in container' unless @default_data_type
19
+ end
20
+
21
+ # Write data object
22
+ #
23
+ # @param [Object] value A compatible data object to be stored against .data property
24
+ # @param [Symbol] data_action The data_action to take when setting data, defaults to :replace
25
+ # @param [:replace] data_action :replace will replace the existing data instance with the incoming data value
26
+ # @param [:append] data_action :append will keep existing data and then new value data over the top
27
+ def set_data(value, data_action: :replace)
28
+ warn("Incompatible data type - #{default_data_type} is incompatible with #{value.class} in set data") unless value.is_a?(default_data_type)
29
+
30
+ case data_action
31
+ when :replace
32
+ @data = value
33
+ when :append
34
+ @data.merge!(value) if @data.is_a?(Hash)
35
+ @data += value if @data.is_a?(Array)
36
+ else
37
+ warn("Unknown data_action: #{data_action}")
38
+ end
39
+ end
40
+
41
+ def clear_data
42
+ @data.clear
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDoc
4
+ # Guarded provides parameter waring and guarding
5
+ #
6
+ # TODO: this could be moved into KType or KGuard
7
+ module Guarded
8
+ Guard = Struct.new(:type, :message)
9
+
10
+ def guard(message)
11
+ errors << Guard.new(:guard, message)
12
+ end
13
+
14
+ def warn(message)
15
+ errors << Guard.new(:warning, message)
16
+ end
17
+ alias warning warn
18
+
19
+ def errors
20
+ @errors ||= []
21
+ end
22
+
23
+ def error_messages
24
+ errors.map(&:message)
25
+ end
26
+
27
+ def valid?
28
+ errors.length.zero?
29
+ end
30
+
31
+ def log_any_messages
32
+ errors.each do |error|
33
+ log.warn error.message if error.type == :warning
34
+ log.error error.message if error.type == :guard
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDoc
4
+ # A container acts a base data object for any data that requires tagging
5
+ # such as unique key, type and namespace.
6
+ module Taggable
7
+ include KLog::Logging
8
+
9
+ attr_reader :tag_options
10
+
11
+ # Tags are provided via an options hash, these tags are remove from the hash as they are read
12
+ #
13
+ # Any container can be uniquely identified via it's key, type, namespace and project_key tags
14
+ #
15
+ # @param [Hash] opts The options
16
+ # @option opts [String|Symbol] project Project Name
17
+ # @option opts [String|Symbol|Array] namespace Namespace or array if namespace is deep nested
18
+ # @option opts [String|Symbol] name Container Name
19
+ # @option opts [String|Symbol] type Type of the container, uses default_container_type if not set
20
+ def initialize_tag(opts)
21
+ @tag_options = opts
22
+ build_tag
23
+ end
24
+
25
+ def tag
26
+ return @tag if defined? @tag
27
+ end
28
+
29
+ # Name of the document (required)
30
+ #
31
+ # Examples: user, account, country
32
+ def key
33
+ @key ||= @tag_options.delete(:key) || SecureRandom.alphanumeric(4)
34
+ end
35
+
36
+ # Type of data
37
+ #
38
+ # Examples by data type
39
+ # :csv, :yaml, :json, :xml
40
+ #
41
+ # Examples by shape of the data in a DSL
42
+ # :entity, :microapp, blueprint
43
+ def type
44
+ @type ||= @tag_options.delete(:type) || default_container_type
45
+ end
46
+
47
+ # Project name
48
+ #
49
+ # Examples
50
+ # :app_name1, :app_name2, :library_name1
51
+ def project
52
+ @project ||= @tag_options.delete(:project) || ''
53
+ end
54
+
55
+ # Namespace(s) what namespace is this document under
56
+ #
57
+ # Example for single path
58
+ # :controllers, :models
59
+ #
60
+ # Example for deep path
61
+ # [:app, :controllers, :admin]
62
+ def namespace
63
+ return @namespace if defined? @namespace
64
+
65
+ ns = @tag_options.delete(:namespace) || []
66
+ @namespace = ns.is_a?(Array) ? ns : [ns]
67
+ end
68
+
69
+ # # Internal data object
70
+ # def data
71
+ # @data ||= @tag_options.delete(:data) || @tag_options.delete(:default_data) || default_data_value
72
+ # # Settings and Table on Model needed access to @data for modification, I don't think this should be a clone
73
+ # # never return the original data object, but at the same time
74
+ # # do not re-clone it every time this accessor is called.
75
+ # # @clone_data ||= @data.clone
76
+ # end
77
+
78
+ # Implement in container
79
+ # def default_container_type
80
+ # :container
81
+ # end
82
+
83
+ # def default_data_value
84
+ # {}
85
+ # end
86
+
87
+ protected
88
+
89
+ # rubocop:disable Metrics/AbcSize
90
+ def debug_container
91
+ log.kv 'tag' , tag , debug_pad_size
92
+ log.kv 'project' , project , debug_pad_size unless project.nil? || project.empty?
93
+ log.kv 'namespace', namespace , debug_pad_size unless namespace.nil? || namespace.empty?
94
+ log.kv 'key' , key , debug_pad_size
95
+ log.kv 'type' , type , debug_pad_size
96
+ # log.kv 'error' , error , debug_kv_pad_size
97
+ end
98
+ # rubocop:enable Metrics/AbcSize
99
+
100
+ def debug_pad_size
101
+ @debug_pad_size ||= @tag_options.delete(:debug_pad_size) || 15
102
+ end
103
+
104
+ private
105
+
106
+ def build_tag
107
+ values = []
108
+ values << project if project
109
+ values += namespace
110
+ values << type if type
111
+ values << key
112
+ values -= [nil, '']
113
+ @tag = values.join('_').to_sym
114
+ end
115
+ end
116
+ end
data/lib/k_doc/model.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module KDoc
4
4
  # Model is a DSL for modeling general purpose data objects
5
5
  #
6
- # A mode can have
6
+ # A model can have
7
7
  # - 0 or more named setting groups each with their key/value pairs
8
8
  # - 0 or more named table groups each with their own columns and rows
9
9
  #
@@ -17,69 +17,26 @@ module KDoc
17
17
  # include KType::NamedFolder
18
18
  # include KType::LayeredFolder
19
19
 
20
- attr_reader :options
21
-
22
- # Create document
23
- #
24
- # @param [String|Symbol] name Name of the document
25
- # @param args[0] Type of the document, defaults to KDoc:: FakeOpinion.new.default_model_type if not set
26
- # @param default: Default value (using named params), as above
27
- def initialize(key = nil, **options, &block)
28
- super(key: key, type: options[:type] || KDoc.opinion.default_model_type) # , namespace: options[:namespace], project_key: options[:project_key])
29
- initialize_attributes(**options)
30
-
31
- @block = block if block_given?
20
+ def initialize(key = nil, **opts, &block)
21
+ super(**{ key: key }.merge(opts), &block)
32
22
  end
33
23
 
34
- # NOTE: Can this be moved out of the is object?
35
- def execute_block(run_actions: nil)
36
- return if @block.nil?
37
-
38
- # The DSL actions method will only run on run_actions: true
39
- @run_actions = run_actions
40
-
41
- instance_eval(&@block)
42
-
43
- on_action if run_actions && respond_to?(:on_action)
44
- # rescue KDoc::Error => e
45
- # puts('KDoc::Error in document')
46
- # puts "key #{unique_key}"
47
- # # puts "file #{KUtil.data.console_file_hyperlink(resource.file, resource.file)}"
48
- # puts(e.message)
49
- # @error = e
50
- # raise
51
- rescue StandardError => e
52
- log.error('Standard error in document')
53
- puts "key #{unique_key}"
54
- # puts "file #{KUtil.data.console_file_hyperlink(resource.file, resource.file)}"
55
- log.error(e.message)
56
- @error = e
57
- # log.exception exception2
58
- raise
59
- ensure
60
- @run_actions = nil
61
- end
62
-
63
- # Move this up to k_manager
64
- # def unique_key
65
- # @unique_key ||= KDoc.util.build_unique_key(key, type, namespace)
66
- # end
67
-
68
- def settings(key = nil, **options, &block)
69
- options ||= {}
24
+ # Need to look at Director as an alternative to this technique
25
+ def settings(key = nil, **setting_opts, &block)
26
+ setting_opts ||= {}
70
27
 
71
- opts = {}.merge(@options) # Data Options
72
- .merge(options) # Settings Options
73
- .merge(parent: self)
28
+ setting_opts = {}.merge(opts) # Container options
29
+ .merge(setting_opts) # Settings setting_opts
30
+ .merge(parent: self)
74
31
 
75
- settings_instance(@data, key, **opts, &block)
32
+ settings_instance(data, key, **setting_opts, &block)
76
33
  # settings.run_decorators(opts)
77
34
  end
78
35
 
79
- def table(key = :table, **options, &block)
36
+ def table(key = :table, **opts, &block)
80
37
  # NEED to add support for run_decorators I think
81
- options.merge(parent: self)
82
- table_instance(@data, key, **options, &block)
38
+ opts.merge(parent: self)
39
+ table_instance(data, key, **opts, &block)
83
40
  end
84
41
  alias rows table
85
42
 
@@ -89,6 +46,16 @@ module KDoc
89
46
  # KDoc::Builder::Shotstack.new(@data, key, &block)
90
47
  # end
91
48
 
49
+ # Need to move this down to container
50
+ # Need to use some sort of cache invalidation to know if the internal data has been altered
51
+ def odata
52
+ @odata ||= data_struct
53
+ end
54
+
55
+ def oraw
56
+ @oraw ||= raw_data_struct
57
+ end
58
+
92
59
  def data_struct
93
60
  KUtil.data.to_open_struct(data)
94
61
  end
@@ -98,9 +65,13 @@ module KDoc
98
65
  KUtil.data.to_open_struct(raw_data)
99
66
  end
100
67
 
68
+ def default_container_type
69
+ KDoc.opinion.default_model_type
70
+ end
71
+
101
72
  def get_node_type(node_name)
102
73
  node_name = KUtil.data.clean_symbol(node_name)
103
- node_data = @data[node_name]
74
+ node_data = data[node_name]
104
75
 
105
76
  raise KDoc::Error, "Node not found: #{node_name}" if node_data.nil?
106
77
 
@@ -144,46 +115,26 @@ module KDoc
144
115
 
145
116
  def debug_header
146
117
  log.heading self.class.name
147
- log.kv 'key', key
148
- log.kv 'type', type
149
- # log.kv 'namespace', namespace
150
- log.kv 'error', error
151
-
118
+ debug_container
152
119
  debug_header_keys
153
120
 
154
121
  log.line
155
122
  end
156
123
 
157
124
  def debug_header_keys
158
- options&.keys&.reject { |k| k == :namespace }&.each do |key|
159
- log.kv key, options[key]
125
+ opts&.keys&.reject { |k| k == :namespace }&.each do |key|
126
+ log.kv key, opts[key]
160
127
  end
161
128
  end
162
129
 
163
130
  private
164
131
 
165
- def initialize_attributes(**options)
166
- @options = options || {}
167
- @parent = slice_option(:parent)
168
-
169
- # Most documents live within a hash, some tabular documents such as CSV will use an []
170
- @data = slice_option(:default_data) || {}
171
- end
172
-
173
- def settings_instance(data, key, **options, &block)
174
- KDoc.opinion.settings_class.new(data, key, **options, &block)
175
- end
176
-
177
- def table_instance(data, key, **options, &block)
178
- KDoc.opinion.table_class.new(data, key, **options, &block)
132
+ def settings_instance(data, key, **opts, &block)
133
+ KDoc.opinion.settings_class.new(data, key, **opts, &block)
179
134
  end
180
135
 
181
- def slice_option(key)
182
- return nil unless @options.key?(key)
183
-
184
- result = @options[key]
185
- @options.delete(key)
186
- result
136
+ def table_instance(data, key, **opts, &block)
137
+ KDoc.opinion.table_class.new(data, key, **opts, &block)
187
138
  end
188
139
  end
189
140
  end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ # module KDoc
4
+ # # Model is a DSL for modeling general purpose data objects
5
+ # #
6
+ # # A model can have
7
+ # # - 0 or more named setting groups each with their key/value pairs
8
+ # # - 0 or more named table groups each with their own columns and rows
9
+ # #
10
+ # # A settings group without a name will default to name: :settings
11
+ # # A table group without a name will default to name: :table
12
+ # class Model < KDoc::Container
13
+ # include KLog::Logging
14
+
15
+ # # include KType::Error
16
+ # # include KType::ManagedState
17
+ # # include KType::NamedFolder
18
+ # # include KType::LayeredFolder
19
+
20
+ # attr_reader :options
21
+
22
+ # # Create document
23
+ # #
24
+ # # @param [String|Symbol] name Name of the document
25
+ # # @param args[0] Type of the document, defaults to KDoc:: FakeOpinion.new.default_model_type if not set
26
+ # # @param default: Default value (using named params), as above
27
+ # def initialize(key = nil, **options, &block)
28
+ # super(key: key, type: options[:type] || KDoc.opinion.default_model_type) # , namespace: options[:namespace], project_key: options[:project_key])
29
+ # initialize_attributes(**options)
30
+
31
+ # @block = block if block_given?
32
+ # end
33
+
34
+ # # NOTE: Can this be moved out of the is object?
35
+ # def execute_block(run_actions: nil)
36
+ # return if @block.nil?
37
+
38
+ # # The DSL actions method will only run on run_actions: true
39
+ # @run_actions = run_actions
40
+
41
+ # instance_eval(&@block)
42
+
43
+ # on_action if run_actions && respond_to?(:on_action)
44
+ # # rescue KDoc::Error => e
45
+ # # puts('KDoc::Error in document')
46
+ # # puts "key #{unique_key}"
47
+ # # # puts "file #{KUtil.data.console_file_hyperlink(resource.file, resource.file)}"
48
+ # # puts(e.message)
49
+ # # @error = e
50
+ # # raise
51
+ # rescue StandardError => e
52
+ # log.error('Standard error in document')
53
+ # # puts "key #{unique_key}"
54
+ # # puts "file #{KUtil.data.console_file_hyperlink(resource.file, resource.file)}"
55
+ # log.error(e.message)
56
+ # @error = e
57
+ # # log.exception exception2
58
+ # raise
59
+ # ensure
60
+ # @run_actions = nil
61
+ # end
62
+
63
+ # def settings(key = nil, **options, &block)
64
+ # options ||= {}
65
+
66
+ # opts = {}.merge(@options) # Data Options
67
+ # .merge(options) # Settings Options
68
+ # .merge(parent: self)
69
+
70
+ # settings_instance(@data, key, **opts, &block)
71
+ # # settings.run_decorators(opts)
72
+ # end
73
+
74
+ # def table(key = :table, **options, &block)
75
+ # # NEED to add support for run_decorators I think
76
+ # options.merge(parent: self)
77
+ # table_instance(@data, key, **options, &block)
78
+ # end
79
+ # alias rows table
80
+
81
+ # # Sweet add-on would be builders
82
+ # # def builder(key, &block)
83
+ # # # example
84
+ # # KDoc::Builder::Shotstack.new(@data, key, &block)
85
+ # # end
86
+
87
+ # # Need to move this down to container
88
+ # # Need to use some sort of cache invalidation to know if the internal data has been altered
89
+ # def odata
90
+ # @odata ||= data_struct
91
+ # end
92
+
93
+ # def oraw
94
+ # @oraw ||= raw_data_struct
95
+ # end
96
+
97
+ # def data_struct
98
+ # KUtil.data.to_open_struct(data)
99
+ # end
100
+ # # alias d data_struct
101
+
102
+ # def raw_data_struct
103
+ # KUtil.data.to_open_struct(raw_data)
104
+ # end
105
+
106
+ # def get_node_type(node_name)
107
+ # node_name = KUtil.data.clean_symbol(node_name)
108
+ # node_data = @data[node_name]
109
+
110
+ # raise KDoc::Error, "Node not found: #{node_name}" if node_data.nil?
111
+
112
+ # if node_data.keys.length == 2 && (node_data.key?('fields') && node_data.key?('rows'))
113
+ # :table
114
+ # else
115
+ # :settings
116
+ # end
117
+ # end
118
+
119
+ # # Removes any meta data eg. "fields" from a table and just returns the raw data
120
+ # # REFACTOR: IT MAY BE BEST TO MOVE raw_data into each of the node_types
121
+ # def raw_data
122
+ # # REFACT, what if this is CSV, meaning it is just an array?
123
+ # # add specs
124
+ # result = data
125
+
126
+ # result.each_key do |key|
127
+ # # ANTI: get_node_type uses @data while we are using @data.clone here
128
+ # result[key] = if get_node_type(key) == :table
129
+ # # Old format was to keep the rows and delete the fields
130
+ # # Now the format is to pull the row_value up to the key and remove rows and fields
131
+ # # result[key].delete('fields')
132
+ # result[key]['rows']
133
+ # else
134
+ # result[key]
135
+ # end
136
+ # end
137
+
138
+ # result
139
+ # end
140
+
141
+ # # Move this out to the logger function when it has been refactor
142
+ # def debug(include_header: false)
143
+ # debug_header if include_header
144
+
145
+ # # tp dsls.values, :k_key, :k_type, :state, :save_at, :last_at, :data, :last_data, :source, { :file => { :width => 150 } }
146
+ # # puts JSON.pretty_generate(data)
147
+ # log.o(raw_data_struct)
148
+ # end
149
+
150
+ # def debug_header
151
+ # log.heading self.class.name
152
+ # debug_container
153
+ # debug_header_keys
154
+
155
+ # log.line
156
+ # end
157
+
158
+ # def debug_header_keys
159
+ # options&.keys&.reject { |k| k == :namespace }&.each do |key|
160
+ # log.kv key, options[key]
161
+ # end
162
+ # end
163
+
164
+ # private
165
+
166
+ # def initialize_attributes(**options)
167
+ # @options = options || {}
168
+ # # Is parent a part of model, or is it part of k_manager::document_taggable (NOT SURE IF IT IS USED YET)
169
+ # @parent = slice_option(:parent)
170
+
171
+ # # Most documents live within a hash, some tabular documents such as CSV will use an []
172
+ # @data = slice_option(:default_data) || {}
173
+ # end
174
+
175
+ # def settings_instance(data, key, **options, &block)
176
+ # KDoc.opinion.settings_class.new(data, key, **options, &block)
177
+ # end
178
+
179
+ # def table_instance(data, key, **options, &block)
180
+ # KDoc.opinion.table_class.new(data, key, **options, &block)
181
+ # end
182
+
183
+ # def slice_option(key)
184
+ # return nil unless @options.key?(key)
185
+
186
+ # result = @options[key]
187
+ # @options.delete(key)
188
+ # result
189
+ # end
190
+ # end
191
+ # end
data/lib/k_doc/table.rb CHANGED
@@ -25,10 +25,7 @@ module KDoc
25
25
  #
26
26
  # The older format of an array is supported via a splat conversion
27
27
  def fields(*field_definitions)
28
- if field_definitions.length == 1 && field_definitions[0].is_a?(Array)
29
- log.warn('avoid supplying field definitions with array. *Splat fields is the preferred technique.')
30
- field_definitions = *field_definitions[0]
31
- end
28
+ field_definitions = *field_definitions[0] if field_definitions.length == 1 && field_definitions[0].is_a?(Array)
32
29
 
33
30
  fields = @data[@name]['fields']
34
31
 
@@ -55,14 +52,15 @@ module KDoc
55
52
  hash[f['name']] = f['default']
56
53
  end
57
54
 
55
+ # TODO: clean_symbol should be an option that is turned on or off for the table
58
56
  # Override with positional arguments
59
57
  args.each_with_index do |arg, i|
60
- row[fields[i]['name']] = KUtil.data.clean_symbol(arg)
58
+ row[fields[i]['name']] = arg # KUtil.data.clean_symbol(arg)
61
59
  end
62
60
 
63
61
  # Override with named args
64
62
  named_args.each_key do |key|
65
- row[key.to_s] = named_args[key]
63
+ row[key.to_s] = named_args[key] # KUtil.data.clean_symbol(named_args[key])
66
64
  end
67
65
 
68
66
  @data[@name]['rows'] << row
data/lib/k_doc/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KDoc
4
- VERSION = '0.0.16'
4
+ VERSION = '0.0.22'
5
5
  end