k_doc 0.0.18 → 0.0.24

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,50 @@
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, log: false)
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 error_hash
28
+ errors.map(&:to_h)
29
+ end
30
+
31
+ # TODO: Add these predicates
32
+ # def errors?
33
+ # def warnings?
34
+
35
+ def valid?
36
+ errors.length.zero?
37
+ end
38
+
39
+ def log_any_messages
40
+ errors.each do |error|
41
+ log.warn error.message if error.type == :warning
42
+ log.error error.message if error.type == :guard
43
+ end
44
+ end
45
+
46
+ def clear_errors
47
+ errors.clear
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,118 @@
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
+ # TODO: rename to area (or area root namespace)
48
+ # Project name
49
+ #
50
+ # Examples
51
+ # :app_name1, :app_name2, :library_name1
52
+ def project
53
+ @project ||= @tag_options.delete(:project) || ''
54
+ end
55
+
56
+ # Namespace(s) what namespace is this document under
57
+ #
58
+ # Example for single path
59
+ # :controllers, :models
60
+ #
61
+ # Example for deep path
62
+ # [:app, :controllers, :admin]
63
+ def namespace
64
+ return @namespace if defined? @namespace
65
+
66
+ ns = @tag_options.delete(:namespace) || []
67
+ @namespace = ns.is_a?(Array) ? ns : [ns]
68
+ end
69
+
70
+ # # Internal data object
71
+ # def data
72
+ # @data ||= @tag_options.delete(:data) || @tag_options.delete(:default_data) || default_data_value
73
+ # # Settings and Table on Model needed access to @data for modification, I don't think this should be a clone
74
+ # # never return the original data object, but at the same time
75
+ # # do not re-clone it every time this accessor is called.
76
+ # # @clone_data ||= @data.clone
77
+ # end
78
+
79
+ # Implement in container
80
+ # def default_container_type
81
+ # :container
82
+ # end
83
+
84
+ # def default_data_value
85
+ # {}
86
+ # end
87
+
88
+ protected
89
+
90
+ # rubocop:disable Metrics/AbcSize
91
+ def debug_container
92
+ log.kv 'tag' , tag , debug_pad_size
93
+ log.kv 'project' , project , debug_pad_size unless project.nil? || project.empty?
94
+ log.kv 'namespace' , namespace , debug_pad_size unless namespace.nil? || namespace.empty?
95
+ log.kv 'key' , key , debug_pad_size
96
+ log.kv 'type' , type , debug_pad_size
97
+ log.kv 'class type' , self.class.name , debug_pad_size
98
+ # log.kv 'error' , error , debug_kv_pad_size
99
+ end
100
+ # rubocop:enable Metrics/AbcSize
101
+
102
+ def debug_pad_size
103
+ @debug_pad_size ||= @tag_options.delete(:debug_pad_size) || 20
104
+ end
105
+
106
+ private
107
+
108
+ def build_tag
109
+ values = []
110
+ values << project if project
111
+ values += namespace
112
+ values << key
113
+ values << type if type
114
+ values -= [nil, '']
115
+ @tag = values.join('_').to_sym
116
+ end
117
+ end
118
+ 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,64 +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
24
+ # Need to look at Director as an alternative to this technique
25
+ def settings(key = nil, **setting_opts, &block)
26
+ setting_opts ||= {}
62
27
 
63
- def settings(key = nil, **options, &block)
64
- options ||= {}
28
+ setting_opts = {}.merge(opts) # Container options
29
+ .merge(setting_opts) # Settings setting_opts
30
+ .merge(parent: self)
65
31
 
66
- opts = {}.merge(@options) # Data Options
67
- .merge(options) # Settings Options
68
- .merge(parent: self)
69
-
70
- settings_instance(@data, key, **opts, &block)
32
+ settings_instance(data, key, **setting_opts, &block)
71
33
  # settings.run_decorators(opts)
72
34
  end
73
35
 
74
- def table(key = :table, **options, &block)
36
+ def table(key = :table, **opts, &block)
75
37
  # NEED to add support for run_decorators I think
76
- options.merge(parent: self)
77
- table_instance(@data, key, **options, &block)
38
+ opts.merge(parent: self)
39
+ table_instance(data, key, **opts, &block)
78
40
  end
79
41
  alias rows table
80
42
 
@@ -84,6 +46,16 @@ module KDoc
84
46
  # KDoc::Builder::Shotstack.new(@data, key, &block)
85
47
  # end
86
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
+
87
59
  def data_struct
88
60
  KUtil.data.to_open_struct(data)
89
61
  end
@@ -93,9 +65,13 @@ module KDoc
93
65
  KUtil.data.to_open_struct(raw_data)
94
66
  end
95
67
 
68
+ def default_container_type
69
+ KDoc.opinion.default_model_type
70
+ end
71
+
96
72
  def get_node_type(node_name)
97
73
  node_name = KUtil.data.clean_symbol(node_name)
98
- node_data = @data[node_name]
74
+ node_data = data[node_name]
99
75
 
100
76
  raise KDoc::Error, "Node not found: #{node_name}" if node_data.nil?
101
77
 
@@ -139,47 +115,26 @@ module KDoc
139
115
 
140
116
  def debug_header
141
117
  log.heading self.class.name
142
- log.kv 'key' , key , 15
143
- log.kv 'type' , type , 15
144
- # log.kv 'namespace', namespace
145
- log.kv 'error' , error , 15
146
-
118
+ debug_container
147
119
  debug_header_keys
148
120
 
149
121
  log.line
150
122
  end
151
123
 
152
124
  def debug_header_keys
153
- options&.keys&.reject { |k| k == :namespace }&.each do |key|
154
- log.kv key, options[key]
125
+ opts&.keys&.reject { |k| k == :namespace }&.each do |key|
126
+ log.kv key, opts[key]
155
127
  end
156
128
  end
157
129
 
158
130
  private
159
131
 
160
- def initialize_attributes(**options)
161
- @options = options || {}
162
- # Is parent a part of model, or is it part of k_manager::document_taggable
163
- @parent = slice_option(:parent)
164
-
165
- # Most documents live within a hash, some tabular documents such as CSV will use an []
166
- @data = slice_option(:default_data) || {}
167
- end
168
-
169
- def settings_instance(data, key, **options, &block)
170
- KDoc.opinion.settings_class.new(data, key, **options, &block)
132
+ def settings_instance(data, key, **opts, &block)
133
+ KDoc.opinion.settings_class.new(data, key, **opts, &block)
171
134
  end
172
135
 
173
- def table_instance(data, key, **options, &block)
174
- KDoc.opinion.table_class.new(data, key, **options, &block)
175
- end
176
-
177
- def slice_option(key)
178
- return nil unless @options.key?(key)
179
-
180
- result = @options[key]
181
- @options.delete(key)
182
- result
136
+ def table_instance(data, key, **opts, &block)
137
+ KDoc.opinion.table_class.new(data, key, **opts, &block)
183
138
  end
184
139
  end
185
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
 
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.18'
4
+ VERSION = '0.0.24'
5
5
  end