qippet 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75af562af4fa5c5309e08b59ca945cc430d29d63b61bb2845f6b477c8cfacbd3
4
- data.tar.gz: f2e3a26ed13432de0eb406ce171fc1841f53a744568d8ac619bc416f33d11043
3
+ metadata.gz: 529b8f0f4110561fd449bc25ce3cadb3527722934960c62bb70c379b78e6afc3
4
+ data.tar.gz: 1ebc1a337117b53b3ccee1def99b6d0494e23ea8d84aa91ad688bd8bad3feb35
5
5
  SHA512:
6
- metadata.gz: b986f24cd41c5861deb53f90afda144bb80e70d203cf77a1cdea223d33567cbbe945d03820f67b7c042247fa0f4e8e752ac3ecf31e7b668ca53f0ccd6bca70a1
7
- data.tar.gz: 77eb388abd688187579a3807595c2716c80ce8b8025050ed24fa886d0c717d6750da1bf306c9fca7f63e6cdcbfee03005b8c33535d8d247593bd6647e4fbbc3b
6
+ metadata.gz: c8afc5349405c422bac83dfb7924db87a119c2b8749266a4ddcdc753904328c45f66559861ca1e460a4c329ed78681971de21b85343dc72fa1458ca8a41d0a94
7
+ data.tar.gz: 4348f9a542374f87b51a4bb54ff0ece4779c581374ef352ed707ec0e380e1ab4f9617159cb09c1ae4234dc79379138d74b3fa8be6c32ad0eb6cc9faec1af28f1
data/.rubocop.yml CHANGED
@@ -1,6 +1,5 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 3.0
3
- DisabledByDefault: true
4
3
 
5
4
  Style/StringLiterals:
6
5
  EnforcedStyle: double_quotes
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rmagick"
4
+ require "active_support/core_ext/enumerable"
5
+
6
+ module Qippet
7
+ module Boxes
8
+ # Abstract Class definiton for Box
9
+ class Box
10
+ attr_reader :node, :image
11
+
12
+ PADDING = 40
13
+ PADDING_X = 30
14
+
15
+ def initialize
16
+ self.class.setup_attributes
17
+ end
18
+
19
+ def add_child(child)
20
+ children << child
21
+ end
22
+
23
+ def render
24
+ construct_box_image
25
+ end
26
+
27
+ def children
28
+ @children ||= []
29
+ end
30
+
31
+ def add_attributes(attributes_value)
32
+ allowed_attributes = attributes_value.filter { |key, _| self.class::ALLOWED_ATTRIBUTES.include?(key.to_sym) }
33
+ allowed_attributes.each { |key, val| instance_variable_set "@#{key}", val }
34
+ end
35
+
36
+ def self.setup_attributes
37
+ return unless const_defined? "ALLOWED_ATTRIBUTES"
38
+
39
+ self::ALLOWED_ATTRIBUTES.each { |attr| attr_accessor attr }
40
+ end
41
+
42
+ private
43
+
44
+ def construct_box_image
45
+ children_images = children.map(&:render)
46
+ height = image_height(children_images)
47
+ width = image_width(children_images)
48
+ @image = Magick::Image.new(width, height)
49
+ arrange_children_in_image(children_images)
50
+ end
51
+
52
+ def arrange_children_in_image(children_images)
53
+ acc = 10
54
+ children_images.each do |child_image|
55
+ image.composite! child_image, PADDING_X, acc, Magick::OverCompositeOp
56
+ acc += child_image.rows
57
+ end
58
+ image
59
+ end
60
+
61
+ def image_height(children_images)
62
+ children_images.sum(&:columns) + PADDING
63
+ end
64
+
65
+ def image_width(children_images)
66
+ (children_images.max { |a, b| a.rows <=> b.rows }&.rows || 0) + PADDING
67
+ end
68
+
69
+ # def get_image
70
+ # throw :undefined, "Box Abstract Method not defined"
71
+ # end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./box"
4
+ require "qippet/extract"
5
+ require "qippet/colors"
6
+ require "rouge"
7
+
8
+ module Qippet
9
+ module Boxes
10
+ # Box for creating code
11
+ class CodeBox < Box
12
+ # Class to formate code tokens
13
+ class BareColorFormatter < Rouge::Formatter
14
+ def stream(tokens)
15
+ token_lines(tokens).with_index do |line_tokens, lineno|
16
+ line_tokens_with_color = line_tokens.map do |token|
17
+ token_color = Colors.get_color_by_shortname(token[0].shortname)
18
+ [token_color, *token]
19
+ end
20
+
21
+ yield line_tokens_with_color, lineno
22
+ end
23
+ end
24
+ end
25
+
26
+ attr_reader :code_content, :code_lines, :show_line, :writer, :tokenizer, :formatter
27
+
28
+ ALLOWED_ATTRIBUTES = %i[source path range].freeze
29
+
30
+ TAB_SPACE = 20
31
+ CODE_LINE_HEIGHT = 19
32
+ CODE_CHAR_WIDTH = 8
33
+
34
+ def initialize
35
+ super
36
+ @show_line = false
37
+ end
38
+
39
+ def add_child(*)
40
+ throw :no_children_for_node, "Code Box cannot contain chidren"
41
+ end
42
+
43
+ def render
44
+ fetch_code_from_source
45
+ extract_line_range
46
+ return nil if code_content.nil?
47
+
48
+ setup_syntax_highliter
49
+ setup_writer
50
+ super
51
+ write_code_on_image
52
+ end
53
+
54
+ private
55
+
56
+ def fetch_code_from_source
57
+ @code_content = source == "github" ? Extract.from_github(path) : Extract.from_file(path)
58
+ return nil if code_content.nil?
59
+
60
+ @code_lines = code_content.split("\n")
61
+ end
62
+
63
+ def extract_line_range
64
+ return if range.nil?
65
+
66
+ ranges = range.split(",").first(2).map { |val| Integer(val.strip) }
67
+ @code_lines = @code_lines[(ranges[0] || 0)..(ranges[1] || @code_lines.length)]
68
+ end
69
+
70
+ def write_code_on_image
71
+ @row_position = PADDING + CODE_LINE_HEIGHT
72
+
73
+ formatter.format(tokenizer.lex(code_lines.join("\n"))) do |line, index|
74
+ @column_position = PADDING
75
+ add_line_number(index + 1) if show_line
76
+
77
+ line.each do |token_with_color|
78
+ write_token(token_with_color)
79
+ end
80
+ @row_position += CODE_LINE_HEIGHT
81
+ end
82
+ image
83
+ end
84
+
85
+ def write_token(token_with_color)
86
+ color, token_type, token = token_with_color
87
+ token.prepend("\\") if token_type.shortname.blank?
88
+
89
+ escape_special_characters(token)
90
+ token_width = writer.get_type_metrics(token).width
91
+
92
+ writer.annotate(image, 0, 0, @column_position, @row_position, token) { |opt| opt.fill = color }
93
+ @column_position += token_width
94
+ end
95
+
96
+ def setup_syntax_highliter
97
+ @tokenizer = Rouge::Lexers::Ruby.new
98
+ @formatter = BareColorFormatter.new
99
+ end
100
+
101
+ def escape_special_characters(str)
102
+ str.gsub!(/%/, "%" => "%%")
103
+ end
104
+
105
+ def add_line_number(number)
106
+ writer.annotate(image, 0, 0, @column_position, @row_position, number.to_s) { |opt| opt.fill = "black" }
107
+ @column_position += TAB_SPACE
108
+ end
109
+
110
+ def add_line_indentation(line)
111
+ line.chars.each do |c|
112
+ break if c != " "
113
+
114
+ @row_position += CODE_CHAR_WIDTH
115
+ end
116
+ end
117
+
118
+ def setup_writer
119
+ @writer = Magick::Draw.new
120
+ @writer.font_family = "helvetica"
121
+ @writer.pointsize = 15
122
+ end
123
+
124
+ def image_height(*)
125
+ (code_lines.length * CODE_LINE_HEIGHT) + (2 * PADDING)
126
+ end
127
+
128
+ def image_width(*)
129
+ code_lines.map { |line| line.blank? ? 0 : writer.get_type_metrics(line).width }.max + (2 * PADDING)
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./box"
4
+
5
+ module Qippet
6
+ module Boxes
7
+ # Container Class create from the "q" keyword
8
+ class ContainerBox < Box
9
+ ALLOWED_ATTRIBUTES = [].freeze
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./box"
4
+
5
+ module Qippet
6
+ module Boxes
7
+ # Text box created from the "text" keyword
8
+ class TextBox < Box
9
+ ALLOWED_ATTRIBUTES = [].freeze
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require "qippet/boxes/container_box"
5
+ require "qippet/boxes/text_box"
6
+ require "qippet/boxes/code_box"
7
+
8
+ module Qippet
9
+ module Builder
10
+ # Builder from XML content to box
11
+ module Xml
12
+ extend ActiveSupport::Autoload
13
+ extend self
14
+
15
+ def build(content = "")
16
+ structured_content = create_structure_from_xml_content(content)
17
+ return nil if structured_content.nil?
18
+
19
+ setup_node(structured_content[:root])
20
+ end
21
+
22
+ private
23
+
24
+ def create_structure_from_xml_content(content)
25
+ return nil if content.empty?
26
+
27
+ parsed_data = Nokogiri::XML.parse(content)
28
+ parsed_data.deconstruct_keys([:root])
29
+ end
30
+
31
+ def setup_node(node)
32
+ return nil unless node.presence
33
+
34
+ node.content.strip!
35
+ node_box = get_node_box(node.name.presence)
36
+ add_box_attributes(node_box, node.attribute_nodes)
37
+ return node_box if node.children.empty?
38
+
39
+ node.children.each do |child|
40
+ box = setup_node(child)
41
+ node_box.add_child box if box.present?
42
+ end
43
+
44
+ node_box
45
+ end
46
+
47
+ def add_box_attributes(box, attribute_nodes)
48
+ box.add_attributes(attribute_nodes.map { |attr| [attr.name, attr.value] })
49
+ end
50
+
51
+ def get_node_box(node_name)
52
+ case node_name
53
+ when "q"
54
+ Qippet::Boxes::ContainerBox.new
55
+ when "code"
56
+ Qippet::Boxes::CodeBox.new
57
+ when "text"
58
+ Qippet::Boxes::TextBox.new
59
+ else
60
+ throw :unrecognize_structure, "Structure contains unrecognize element."
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext/object/blank"
5
+
6
+ # Qippet
7
+ module Qippet
8
+ extend ActiveSupport::Autoload
9
+
10
+ # Builder Class
11
+ module Builder
12
+ extend ActiveSupport::Autoload
13
+
14
+ autoload :Xml
15
+
16
+ class << self
17
+ def build(content)
18
+ Xml.build(content)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qippet
4
+ # Module contains all colors for code
5
+ module Colors
6
+ DEFAULT_COLORS = {
7
+ w: "red",
8
+ o: "black",
9
+ nb: "blue",
10
+ c1: "gray",
11
+ s2: "#FF7F50",
12
+ no: "#50D0FF"
13
+ }.freeze
14
+
15
+ def self.get_color_by_shortname(shortname)
16
+ DEFAULT_COLORS.fetch(shortname.to_sym, "black")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "net/http"
5
+ require "base64"
6
+ require "json"
7
+ require "pathname"
8
+
9
+ module Qippet
10
+ # Extract Class
11
+ class Extract
12
+ class << self
13
+ GITHUB_BASE_API_URL = "https://api.github.com/repos/"
14
+
15
+ def from_file(path)
16
+ path = Pathname.new(Dir.pwd).join(path)
17
+ read_file(path)
18
+ end
19
+
20
+ def from_github(path = "rails/rails/contents/version.rb")
21
+ url = URI("#{GITHUB_BASE_API_URL}#{path}")
22
+ response = Net::HTTP.get_response(url)
23
+ response = JSON.parse(response.body)
24
+ Base64.decode64(response["content"])
25
+ rescue StandardError
26
+ nil
27
+ end
28
+
29
+ private
30
+
31
+ def read_file(path)
32
+ File.read(path)
33
+ rescue Errno::ENOENT, Errno::EISDIR
34
+ nil
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Qippet
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
data/lib/qippet.rb CHANGED
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support"
3
4
  require_relative "qippet/version"
4
5
 
5
6
  # Main object for Qippet. This is the main entry point for
6
7
  # the application.
7
8
  module Qippet
9
+ extend ActiveSupport::Autoload
10
+
11
+ # autoload :Xml, "qippet/extraction/xml"
12
+ autoload :Extract
13
+ autoload :Builder
8
14
  class Error < StandardError; end
9
15
 
10
16
  # Configuration class Object
@@ -27,5 +33,17 @@ module Qippet
27
33
  def configure
28
34
  yield configuration
29
35
  end
36
+
37
+ def generate(path, output_file)
38
+ yield configuration if block_given?
39
+
40
+ extracted = Extract.from_file(path)
41
+ node = Builder.build(extracted)
42
+ puts "File does not contain a valid Qippet structure" if node.nil?
43
+
44
+ node&.render
45
+ # result.write(output_file)
46
+ output_file
47
+ end
30
48
  end
31
49
  end
metadata CHANGED
@@ -1,15 +1,89 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qippet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Unegbu Kingsley
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-11 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-05-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 7.1.3.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '7.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 7.1.3.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: nokogiri
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.16'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.16.5
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.16'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.16.5
53
+ - !ruby/object:Gem::Dependency
54
+ name: rmagick
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '5.5'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '5.5'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rouge
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '4.2'
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 4.2.1
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '4.2'
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 4.2.1
13
87
  description: Create beautify code snippet image.
14
88
  email: kingsobino@gmail.com
15
89
  executables: []
@@ -24,6 +98,14 @@ files:
24
98
  - README.md
25
99
  - Rakefile
26
100
  - lib/qippet.rb
101
+ - lib/qippet/boxes/box.rb
102
+ - lib/qippet/boxes/code_box.rb
103
+ - lib/qippet/boxes/container_box.rb
104
+ - lib/qippet/boxes/text_box.rb
105
+ - lib/qippet/builder.rb
106
+ - lib/qippet/builder/xml.rb
107
+ - lib/qippet/colors.rb
108
+ - lib/qippet/extract.rb
27
109
  - lib/qippet/version.rb
28
110
  - sig/qippet.rbs
29
111
  homepage: https://github.com/Urchmaney/qippet