docx 0.3.0 → 0.8.0

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
- SHA1:
3
- metadata.gz: 436e2d77b50265c75cd9be63a2957a5f71da61e5
4
- data.tar.gz: 6b14b8f1a8ec88a2f7286777f4238da6c95748e0
2
+ SHA256:
3
+ metadata.gz: b777dc986b688750a284502115839f0c1fa58b50d562f1c098155e4386afe85b
4
+ data.tar.gz: 6de4259ddd408787823b60557535906aacc3357d54794941df3ef7e58105c713
5
5
  SHA512:
6
- metadata.gz: aa87a1a97fab2a49e023bae4dfe1000477ed20f7cf09df9d5f63f46caf8a8e238533f2df680684f0da3530b1031a827f7d4c54fa2520a72c153214db547b0e26
7
- data.tar.gz: 856503643daa3f057cd4625fc8f63660748e476f71712005a841721bf392d9322430dd6d5988a3c6549ef1d7668c12de49faeacb032ffe19b2e47a5d2504292b
6
+ metadata.gz: 8dd7ac5d3396372c3c5e21c1a8f73e6090854397874b5f69fe26f8e289c7417dd963fd1767434c8b6af920de96a1ea7a14d165e4ea9cb027f087f2b5c5248e02
7
+ data.tar.gz: '084b2be2ebf7072a472802ca449806063930723d244cda22ff6e73bcee24d9a6689d0daaf531bb2429372f639714ed8f8d18f0925c8800cbd8ff1bb313391610'
data/README.md CHANGED
@@ -1,14 +1,37 @@
1
1
  # docx
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/docx.svg)](https://badge.fury.io/rb/docx)
4
+ [![Ruby](https://github.com/ruby-docx/docx/workflows/Ruby/badge.svg)](https://github.com/ruby-docx/docx/actions?query=workflow%3ARuby)
5
+ [![Coverage Status](https://coveralls.io/repos/github/ruby-docx/docx/badge.svg?branch=master)](https://coveralls.io/github/ruby-docx/docx?branch=master)
6
+ [![Gitter](https://badges.gitter.im/ruby-docx/community.svg)](https://gitter.im/ruby-docx/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
7
+
3
8
  A ruby library/gem for interacting with `.docx` files. currently capabilities include reading paragraphs/bookmarks, inserting text at bookmarks, reading tables/rows/columns/cells and saving the document.
4
9
 
5
10
  ## Usage
6
11
 
12
+ ### Prerequisites
13
+
14
+ - Ruby 2.6 or later
15
+
7
16
  ### Install
8
17
 
9
- Requires ruby (tested with 2.1.1)
18
+ Add the following line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'docx'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ ```shell
27
+ bundle install
28
+ ```
10
29
 
11
- gem 'docx', '~> 0.2.07', :require => ["docx"]
30
+ Or install it yourself as:
31
+
32
+ ```shell
33
+ gem install docx
34
+ ```
12
35
 
13
36
  ### Reading
14
37
 
@@ -29,6 +52,17 @@ doc.bookmarks.each_pair do |bookmark_name, bookmark_object|
29
52
  end
30
53
  ```
31
54
 
55
+ Don't have a local file but a buffer? Docx handles those to:
56
+
57
+ ```ruby
58
+ require 'docx'
59
+
60
+ # Create a Docx::Document object from a remote file
61
+ doc = Docx::Document.open(buffer)
62
+
63
+ # Everything about reading is the same as shown above
64
+ ```
65
+
32
66
  ### Rendering html
33
67
  ``` ruby
34
68
  require 'docx'
@@ -61,7 +95,7 @@ doc.tables.each do |table|
61
95
  puts cell.text
62
96
  end
63
97
  end
64
-
98
+
65
99
  table.columns.each do |column| # Column-based iteration
66
100
  column.cells.each do |cell|
67
101
  puts cell.text
@@ -89,10 +123,46 @@ doc.paragraphs.each do |p|
89
123
  p.remove! if p.to_s =~ /TODO/
90
124
  end
91
125
 
126
+ # Substitute text, preserving formatting
127
+ doc.paragraphs.each do |p|
128
+ p.each_text_run do |tr|
129
+ tr.substitute('_placeholder_', 'replacement value')
130
+ end
131
+ end
132
+
92
133
  # Save document to specified path
93
134
  doc.save('example-edited.docx')
94
135
  ```
95
136
 
137
+ ### Writing to tables
138
+
139
+ ``` ruby
140
+ require 'docx'
141
+
142
+ # Create a Docx::Document object for our existing docx file
143
+ doc = Docx::Document.open('tables.docx')
144
+
145
+ # Iterate over each table
146
+ doc.tables.each do |table|
147
+ last_row = table.rows.last
148
+
149
+ # Copy last row and insert a new one before last row
150
+ new_row = last_row.copy
151
+ new_row.insert_before(last_row)
152
+
153
+ # Substitute text in each cell of this new row
154
+ new_row.cells.each do |cell|
155
+ cell.paragraphs.each do |paragraph|
156
+ paragraph.each_text_run do |text|
157
+ text.substitute('_placeholder_', 'replacement value')
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ doc.save('tables-edited.docx')
164
+ ```
165
+
96
166
  ### Advanced
97
167
 
98
168
  ``` ruby
@@ -55,7 +55,7 @@ module Docx
55
55
 
56
56
  # Array of text runs contained within paragraph
57
57
  def text_runs
58
- @node.xpath('w:r|w:hyperlink/w:r').map { |r_node| Containers::TextRun.new(r_node, @document_properties) }
58
+ @node.xpath('w:r|w:hyperlink').map { |r_node| Containers::TextRun.new(r_node, @document_properties) }
59
59
  end
60
60
 
61
61
  # Iterate over each text run within a paragraph
@@ -12,17 +12,19 @@ module Docx
12
12
  bold: false,
13
13
  underline: false
14
14
  }
15
-
15
+
16
16
  def self.tag
17
17
  'r'
18
18
  end
19
19
 
20
20
  attr_reader :text
21
21
  attr_reader :formatting
22
-
22
+
23
23
  def initialize(node, document_properties = {})
24
24
  @node = node
25
25
  @text_nodes = @node.xpath('w:t').map {|t_node| Elements::Text.new(t_node) }
26
+ @text_nodes = @node.xpath('w:t|w:r/w:t').map {|t_node| Elements::Text.new(t_node) }
27
+
26
28
  @properties_tag = 'rPr'
27
29
  @text = parse_text || ''
28
30
  @formatting = parse_formatting || DEFAULT_FORMATTING
@@ -38,6 +40,7 @@ module Docx
38
40
  new_t = Elements::Text.create_within(self)
39
41
  new_t.content = content
40
42
  end
43
+ reset_text
41
44
  end
42
45
 
43
46
  # Returns text contained within text run
@@ -45,6 +48,14 @@ module Docx
45
48
  @text_nodes.map(&:content).join('')
46
49
  end
47
50
 
51
+ # Substitute text in text @text_nodes
52
+ def substitute(match, replacement)
53
+ @text_nodes.each do |text_node|
54
+ text_node.content = text_node.content.gsub(match, replacement)
55
+ end
56
+ reset_text
57
+ end
58
+
48
59
  def parse_formatting
49
60
  {
50
61
  italic: !@node.xpath('.//w:i').empty?,
@@ -65,27 +76,46 @@ module Docx
65
76
  styles = {}
66
77
  styles['text-decoration'] = 'underline' if underlined?
67
78
  # No need to be granular with font size down to the span level if it doesn't vary.
68
- styles['font-size'] = "#{font_size}pt" if font_size != @font_size
79
+ styles['font-size'] = "#{font_size}pt" if font_size != @font_size
69
80
  html = html_tag(:span, content: html, styles: styles) unless styles.empty?
81
+ html = html_tag(:a, content: html, attributes: {href: href, target: "_blank"}) if hyperlink?
70
82
  return html
71
83
  end
72
84
 
73
85
  def italicized?
74
86
  @formatting[:italic]
75
87
  end
76
-
88
+
77
89
  def bolded?
78
90
  @formatting[:bold]
79
91
  end
80
-
92
+
81
93
  def underlined?
82
94
  @formatting[:underline]
83
95
  end
84
96
 
97
+ def hyperlink?
98
+ @node.name == 'hyperlink'
99
+ end
100
+
101
+ def href
102
+ @document_properties[:hyperlinks][hyperlink_id]
103
+ end
104
+
105
+ def hyperlink_id
106
+ @node.attributes['id'].value
107
+ end
108
+
85
109
  def font_size
86
110
  size_tag = @node.xpath('w:rPr//w:sz').first
87
111
  size_tag ? size_tag.attributes['val'].value.to_i / 2 : @font_size
88
112
  end
113
+
114
+ private
115
+
116
+ def reset_text
117
+ @text = parse_text
118
+ end
89
119
  end
90
120
  end
91
121
  end
data/lib/docx/document.rb CHANGED
@@ -19,50 +19,61 @@ module Docx
19
19
  # end
20
20
  class Document
21
21
  attr_reader :xml, :doc, :zip, :styles
22
-
23
- def initialize(path, &block)
22
+
23
+ def initialize(path_or_io, options = {})
24
24
  @replace = {}
25
- @zip = Zip::File.open(path)
26
- @document_xml = @zip.read('word/document.xml')
27
- @doc = Nokogiri::XML(@document_xml)
28
- @styles_xml = @zip.read('word/styles.xml')
29
- @styles = Nokogiri::XML(@styles_xml)
30
- if block_given?
31
- yield self
32
- @zip.close
25
+
26
+ # if path-or_io is string && does not contain a null byte
27
+ if (path_or_io.instance_of?(String) && !/\u0000/.match?(path_or_io))
28
+ @zip = Zip::File.open(path_or_io)
29
+ else
30
+ @zip = Zip::File.open_buffer(path_or_io)
33
31
  end
34
- end
35
32
 
33
+ document = @zip.glob('word/document*.xml').first
34
+ raise Errno::ENOENT if document.nil?
35
+
36
+ @document_xml = document.get_input_stream.read
37
+ @doc = Nokogiri::XML(@document_xml)
38
+ load_styles
39
+ yield(self) if block_given?
40
+ ensure
41
+ @zip.close
42
+ end
36
43
 
37
44
  # This stores the current global document properties, for now
38
45
  def document_properties
39
46
  {
40
- font_size: font_size
47
+ font_size: font_size,
48
+ hyperlinks: hyperlinks
41
49
  }
42
50
  end
43
51
 
44
-
45
52
  # With no associated block, Docx::Document.open is a synonym for Docx::Document.new. If the optional code block is given, it will be passed the opened +docx+ file as an argument and the Docx::Document oject will automatically be closed when the block terminates. The values of the block will be returned from Docx::Document.open.
46
53
  # call-seq:
47
54
  # open(filepath) => file
48
55
  # open(filepath) {|file| block } => obj
49
56
  def self.open(path, &block)
50
- self.new(path, &block)
57
+ new(path, &block)
51
58
  end
52
59
 
53
60
  def paragraphs
54
- @doc.xpath('//w:document//w:body//w:p').map { |p_node| parse_paragraph_from p_node }
61
+ @doc.xpath('//w:document//w:body/w:p').map { |p_node| parse_paragraph_from p_node }
55
62
  end
56
63
 
57
64
  def bookmarks
58
- bkmrks_hsh = Hash.new
65
+ bkmrks_hsh = {}
59
66
  bkmrks_ary = @doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node }
60
67
  # auto-generated by office 2010
61
- bkmrks_ary.reject! {|b| b.name == "_GoBack" }
62
- bkmrks_ary.each {|b| bkmrks_hsh[b.name] = b }
68
+ bkmrks_ary.reject! { |b| b.name == '_GoBack' }
69
+ bkmrks_ary.each { |b| bkmrks_hsh[b.name] = b }
63
70
  bkmrks_hsh
64
71
  end
65
72
 
73
+ def to_xml
74
+ Nokogiri::XML(@document_xml)
75
+ end
76
+
66
77
  def tables
67
78
  @doc.xpath('//w:document//w:body//w:tbl').map { |t_node| parse_table_from t_node }
68
79
  end
@@ -70,10 +81,23 @@ module Docx
70
81
  # Some documents have this set, others don't.
71
82
  # Values are returned as half-points, so to get points, that's why it's divided by 2.
72
83
  def font_size
84
+ return nil unless @styles
85
+
73
86
  size_tag = @styles.xpath('//w:docDefaults//w:rPrDefault//w:rPr//w:sz').first
74
87
  size_tag ? size_tag.attributes['val'].value.to_i / 2 : nil
75
88
  end
76
89
 
90
+ # Hyperlink targets are extracted from the document.xml.rels file
91
+ def hyperlinks
92
+ hyperlink_relationships.each_with_object({}) do |rel, hash|
93
+ hash[rel.attributes['Id'].value] = rel.attributes['Target'].value
94
+ end
95
+ end
96
+
97
+ def hyperlink_relationships
98
+ @rels.xpath("//xmlns:Relationship[contains(@Type,'hyperlink')]")
99
+ end
100
+
77
101
  ##
78
102
  # *Deprecated*
79
103
  #
@@ -92,7 +116,7 @@ module Docx
92
116
 
93
117
  # Output entire document as a String HTML fragment
94
118
  def to_html
95
- paragraphs.map(&:to_html).join('\n')
119
+ paragraphs.map(&:to_html).join("\n")
96
120
  end
97
121
 
98
122
  # Save document to provided path
@@ -102,6 +126,8 @@ module Docx
102
126
  update
103
127
  Zip::OutputStream.open(path) do |out|
104
128
  zip.each do |entry|
129
+ next unless entry.file?
130
+
105
131
  out.put_next_entry(entry.name)
106
132
 
107
133
  if @replace[entry.name]
@@ -114,7 +140,28 @@ module Docx
114
140
  zip.close
115
141
  end
116
142
 
117
- alias_method :text, :to_s
143
+ # Output entire document as a StringIO object
144
+ def stream
145
+ update
146
+ stream = Zip::OutputStream.write_buffer do |out|
147
+ zip.each do |entry|
148
+ next unless entry.file?
149
+
150
+ out.put_next_entry(entry.name)
151
+
152
+ if @replace[entry.name]
153
+ out.write(@replace[entry.name])
154
+ else
155
+ out.write(zip.read(entry.name))
156
+ end
157
+ end
158
+ end
159
+
160
+ stream.rewind
161
+ stream
162
+ end
163
+
164
+ alias text to_s
118
165
 
119
166
  def replace_entry(entry_path, file_contents)
120
167
  @replace[entry_path] = file_contents
@@ -122,13 +169,30 @@ module Docx
122
169
 
123
170
  private
124
171
 
172
+ def load_styles
173
+ @styles_xml = @zip.read('word/styles.xml')
174
+ @styles = Nokogiri::XML(@styles_xml)
175
+ load_rels
176
+ rescue Errno::ENOENT => e
177
+ warn e.message
178
+ nil
179
+ end
180
+
181
+ def load_rels
182
+ rels_entry = @zip.glob('word/_rels/document*.xml.rels').first
183
+ raise Errno::ENOENT unless rels_entry
184
+
185
+ @rels_xml = rels_entry.get_input_stream.read
186
+ @rels = Nokogiri::XML(@rels_xml)
187
+ end
188
+
125
189
  #--
126
190
  # TODO: Flesh this out to be compatible with other files
127
191
  # TODO: Method to set flag on files that have been edited, probably by inserting something at the
128
192
  # end of methods that make edits?
129
193
  #++
130
194
  def update
131
- replace_entry "word/document.xml", doc.serialize(:save_with => 0)
195
+ replace_entry 'word/document.xml', doc.serialize(save_with: 0)
132
196
  end
133
197
 
134
198
  # generate Elements::Containers::Paragraph from paragraph XML node
@@ -5,7 +5,7 @@ module Docx
5
5
  class Bookmark
6
6
  include Element
7
7
  attr_accessor :name
8
-
8
+
9
9
  def self.tag
10
10
  'bookmarkStart'
11
11
  end
@@ -17,14 +17,14 @@ module Docx
17
17
 
18
18
  # Insert text before bookmarkStart node
19
19
  def insert_text_before(text)
20
- text_run = get_run_after
21
- text_run.text = "#{text}#{text_run.text}"
20
+ text_run = get_run_before
21
+ text_run.text = "#{text_run.text}#{text}"
22
22
  end
23
23
 
24
24
  # Insert text after bookmarkStart node
25
25
  def insert_text_after(text)
26
- text_run = get_run_before
27
- text_run.text = "#{text_run.text}#{text}"
26
+ text_run = get_run_after
27
+ text_run.text = "#{text}#{text_run.text}"
28
28
  end
29
29
 
30
30
  # insert multiple lines starting with paragraph containing bookmark node.
@@ -51,7 +51,7 @@ module Docx
51
51
 
52
52
  # Get text run immediately prior to bookmark node
53
53
  def get_run_before
54
- # at_xpath returns the first match found and preceding-sibling returns siblings in the
54
+ # at_xpath returns the first match found and preceding-sibling returns siblings in the
55
55
  # order they appear in the document not the order as they appear when moving out from
56
56
  # the starting node
57
57
  if not (r_nodes = @node.xpath("./preceding-sibling::w:r")).empty?
@@ -14,13 +14,20 @@ module Docx
14
14
  end
15
15
 
16
16
  attr_accessor :node
17
- delegate :at_xpath, :xpath, :to => :@node
18
17
 
19
18
  # TODO: Should create a docx object from this
20
19
  def parent(type = '*')
21
20
  @node.at_xpath("./parent::#{type}")
22
21
  end
23
22
 
23
+ def at_xpath(*args)
24
+ @node.at_xpath(*args)
25
+ end
26
+
27
+ def xpath(*args)
28
+ @node.xpath(*args)
29
+ end
30
+
24
31
  # Get parent paragraph of element
25
32
  def parent_paragraph
26
33
  Elements::Containers::Paragraph.new(parent('w:p'))
@@ -65,8 +72,10 @@ module Docx
65
72
  def html_tag(name, options = {})
66
73
  content = options[:content]
67
74
  styles = options[:styles]
75
+ attributes = options[:attributes]
68
76
 
69
77
  html = "<#{name.to_s}"
78
+
70
79
  unless styles.nil? || styles.empty?
71
80
  styles_array = []
72
81
  styles.each do |property, value|
@@ -74,6 +83,13 @@ module Docx
74
83
  end
75
84
  html << " style=\"#{styles_array.join('')}\""
76
85
  end
86
+
87
+ unless attributes.nil? || attributes.empty?
88
+ attributes.each do |attr_name, attr_value|
89
+ html << " #{attr_name}=\"#{attr_value}\""
90
+ end
91
+ end
92
+
77
93
  html << ">"
78
94
  html << content if content
79
95
  html << "</#{name.to_s}>"
@@ -82,7 +98,7 @@ module Docx
82
98
  module ClassMethods
83
99
  def create_with(element)
84
100
  # Need to somehow get the xml document accessible here by default, but this is alright in the interim
85
- self.new(Nokogiri::XML::Node.new("w:#{self.tag}", element.node))
101
+ self.new(Nokogiri::XML::Node.new("w:#{self.tag}", element.node.document))
86
102
  end
87
103
 
88
104
  def create_within(element)
@@ -93,4 +109,4 @@ module Docx
93
109
  end
94
110
  end
95
111
  end
96
- end
112
+ end
@@ -2,16 +2,22 @@ module Docx
2
2
  module Elements
3
3
  class Text
4
4
  include Element
5
- delegate :content, :content=, :to => :@node
6
5
 
7
6
  def self.tag
8
7
  't'
9
8
  end
10
9
 
10
+ def content
11
+ @node.content
12
+ end
13
+
14
+ def content=(args)
15
+ @node.content = args
16
+ end
11
17
 
12
18
  def initialize(node)
13
19
  @node = node
14
20
  end
15
21
  end
16
22
  end
17
- end
23
+ end
data/lib/docx/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Docx #:nodoc:
2
- VERSION = '0.3.0'
4
+ VERSION = '0.8.0'
3
5
  end
data/lib/docx.rb CHANGED
@@ -4,4 +4,3 @@ module Docx #:nodoc:
4
4
  autoload :Document, 'docx/document'
5
5
  end
6
6
 
7
- require 'docx/core_ext/module'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Hunt
@@ -9,10 +9,10 @@ authors:
9
9
  - Higgins Dragon
10
10
  - Toms Mikoss
11
11
  - Sebastian Wittenkamp
12
- autorequire:
12
+ autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2018-04-02 00:00:00.000000000 Z
15
+ date: 2023-05-20 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: nokogiri
@@ -20,68 +20,76 @@ dependencies:
20
20
  requirements:
21
21
  - - "~>"
22
22
  - !ruby/object:Gem::Version
23
- version: '1.8'
23
+ version: '1.13'
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.8.1
26
+ version: 1.13.0
27
27
  type: :runtime
28
28
  prerelease: false
29
29
  version_requirements: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.8'
33
+ version: '1.13'
34
34
  - - ">="
35
35
  - !ruby/object:Gem::Version
36
- version: 1.8.1
36
+ version: 1.13.0
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rubyzip
39
39
  requirement: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - "~>"
42
42
  - !ruby/object:Gem::Version
43
- version: '1.2'
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 1.2.1
43
+ version: '2.0'
47
44
  type: :runtime
48
45
  prerelease: false
49
46
  version_requirements: !ruby/object:Gem::Requirement
50
47
  requirements:
51
48
  - - "~>"
52
49
  - !ruby/object:Gem::Version
53
- version: '1.2'
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: 1.2.1
50
+ version: '2.0'
57
51
  - !ruby/object:Gem::Dependency
58
- name: rspec
52
+ name: coveralls_reborn
59
53
  requirement: !ruby/object:Gem::Requirement
60
54
  requirements:
61
55
  - - "~>"
62
56
  - !ruby/object:Gem::Version
63
- version: '3.7'
57
+ version: '0.21'
64
58
  type: :development
65
59
  prerelease: false
66
60
  version_requirements: !ruby/object:Gem::Requirement
67
61
  requirements:
68
62
  - - "~>"
69
63
  - !ruby/object:Gem::Version
70
- version: '3.7'
64
+ version: '0.21'
71
65
  - !ruby/object:Gem::Dependency
72
66
  name: rake
73
67
  requirement: !ruby/object:Gem::Requirement
74
68
  requirements:
75
69
  - - "~>"
76
70
  - !ruby/object:Gem::Version
77
- version: '12.3'
71
+ version: '13.0'
72
+ type: :development
73
+ prerelease: false
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '13.0'
79
+ - !ruby/object:Gem::Dependency
80
+ name: rspec
81
+ requirement: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '3.7'
78
86
  type: :development
79
87
  prerelease: false
80
88
  version_requirements: !ruby/object:Gem::Requirement
81
89
  requirements:
82
90
  - - "~>"
83
91
  - !ruby/object:Gem::Version
84
- version: '12.3'
92
+ version: '3.7'
85
93
  description: thin wrapper around rubyzip and nokogiri as a way to get started with
86
94
  docx files
87
95
  email:
@@ -101,7 +109,6 @@ files:
101
109
  - lib/docx/containers/table_column.rb
102
110
  - lib/docx/containers/table_row.rb
103
111
  - lib/docx/containers/text_run.rb
104
- - lib/docx/core_ext/module.rb
105
112
  - lib/docx/document.rb
106
113
  - lib/docx/elements.rb
107
114
  - lib/docx/elements/bookmark.rb
@@ -112,7 +119,7 @@ homepage: https://github.com/chrahunt/docx
112
119
  licenses:
113
120
  - MIT
114
121
  metadata: {}
115
- post_install_message:
122
+ post_install_message:
116
123
  rdoc_options: []
117
124
  require_paths:
118
125
  - lib
@@ -120,16 +127,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
127
  requirements:
121
128
  - - ">="
122
129
  - !ruby/object:Gem::Version
123
- version: '0'
130
+ version: 2.6.0
124
131
  required_rubygems_version: !ruby/object:Gem::Requirement
125
132
  requirements:
126
133
  - - ">="
127
134
  - !ruby/object:Gem::Version
128
135
  version: '0'
129
136
  requirements: []
130
- rubyforge_project:
131
- rubygems_version: 2.6.14
132
- signing_key:
137
+ rubygems_version: 3.4.1
138
+ signing_key:
133
139
  specification_version: 4
134
140
  summary: a ruby library/gem for interacting with .docx files
135
141
  test_files: []
@@ -1,172 +0,0 @@
1
- unless Object.const_defined?("ActiveSupport")
2
- class Module
3
- # Provides a delegate class method to easily expose contained objects' public methods
4
- # as your own. Pass one or more methods (specified as symbols or strings)
5
- # and the name of the target object via the <tt>:to</tt> option (also a symbol
6
- # or string). At least one method and the <tt>:to</tt> option are required.
7
- #
8
- # Delegation is particularly useful with Active Record associations:
9
- #
10
- # class Greeter < ActiveRecord::Base
11
- # def hello
12
- # 'hello'
13
- # end
14
- #
15
- # def goodbye
16
- # 'goodbye'
17
- # end
18
- # end
19
- #
20
- # class Foo < ActiveRecord::Base
21
- # belongs_to :greeter
22
- # delegate :hello, to: :greeter
23
- # end
24
- #
25
- # Foo.new.hello # => "hello"
26
- # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
27
- #
28
- # Multiple delegates to the same target are allowed:
29
- #
30
- # class Foo < ActiveRecord::Base
31
- # belongs_to :greeter
32
- # delegate :hello, :goodbye, to: :greeter
33
- # end
34
- #
35
- # Foo.new.goodbye # => "goodbye"
36
- #
37
- # Methods can be delegated to instance variables, class variables, or constants
38
- # by providing them as a symbols:
39
- #
40
- # class Foo
41
- # CONSTANT_ARRAY = [0,1,2,3]
42
- # @@class_array = [4,5,6,7]
43
- #
44
- # def initialize
45
- # @instance_array = [8,9,10,11]
46
- # end
47
- # delegate :sum, to: :CONSTANT_ARRAY
48
- # delegate :min, to: :@@class_array
49
- # delegate :max, to: :@instance_array
50
- # end
51
- #
52
- # Foo.new.sum # => 6
53
- # Foo.new.min # => 4
54
- # Foo.new.max # => 11
55
- #
56
- # It's also possible to delegate a method to the class by using +:class+:
57
- #
58
- # class Foo
59
- # def self.hello
60
- # "world"
61
- # end
62
- #
63
- # delegate :hello, to: :class
64
- # end
65
- #
66
- # Foo.new.hello # => "world"
67
- #
68
- # Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
69
- # is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
70
- # delegated to.
71
- #
72
- # Person = Struct.new(:name, :address)
73
- #
74
- # class Invoice < Struct.new(:client)
75
- # delegate :name, :address, to: :client, prefix: true
76
- # end
77
- #
78
- # john_doe = Person.new('John Doe', 'Vimmersvej 13')
79
- # invoice = Invoice.new(john_doe)
80
- # invoice.client_name # => "John Doe"
81
- # invoice.client_address # => "Vimmersvej 13"
82
- #
83
- # It is also possible to supply a custom prefix.
84
- #
85
- # class Invoice < Struct.new(:client)
86
- # delegate :name, :address, to: :client, prefix: :customer
87
- # end
88
- #
89
- # invoice = Invoice.new(john_doe)
90
- # invoice.customer_name # => 'John Doe'
91
- # invoice.customer_address # => 'Vimmersvej 13'
92
- #
93
- # If the delegate object is +nil+ an exception is raised, and that happens
94
- # no matter whether +nil+ responds to the delegated method. You can get a
95
- # +nil+ instead with the +:allow_nil+ option.
96
- #
97
- # class Foo
98
- # attr_accessor :bar
99
- # def initialize(bar = nil)
100
- # @bar = bar
101
- # end
102
- # delegate :zoo, to: :bar
103
- # end
104
- #
105
- # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
106
- #
107
- # class Foo
108
- # attr_accessor :bar
109
- # def initialize(bar = nil)
110
- # @bar = bar
111
- # end
112
- # delegate :zoo, to: :bar, allow_nil: true
113
- # end
114
- #
115
- # Foo.new.zoo # returns nil
116
- def delegate(*methods)
117
- options = methods.pop
118
- unless options.is_a?(Hash) && to = options[:to]
119
- raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
120
- end
121
-
122
- prefix, allow_nil = options.values_at(:prefix, :allow_nil)
123
-
124
- if prefix == true && to =~ /^[^a-z_]/
125
- raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
126
- end
127
-
128
- method_prefix = \
129
- if prefix
130
- "#{prefix == true ? to : prefix}_"
131
- else
132
- ''
133
- end
134
-
135
- file, line = caller.first.split(':', 2)
136
- line = line.to_i
137
-
138
- to = to.to_s
139
- to = 'self.class' if to == 'class'
140
-
141
- methods.each do |method|
142
- # Attribute writer methods only accept one argument. Makes sure []=
143
- # methods still accept two arguments.
144
- definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
145
-
146
- if allow_nil
147
- module_eval(<<-EOS, file, line - 2)
148
- def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
149
- if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
150
- #{to}.#{method}(#{definition}) # client.name(*args, &block)
151
- end # end
152
- end # end
153
- EOS
154
- else
155
- exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
156
-
157
- module_eval(<<-EOS, file, line - 1)
158
- def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
159
- #{to}.#{method}(#{definition}) # client.name(*args, &block)
160
- rescue NoMethodError # rescue NoMethodError
161
- if #{to}.nil? # if client.nil?
162
- #{exception} # # add helpful message to the exception
163
- else # else
164
- raise # raise
165
- end # end
166
- end # end
167
- EOS
168
- end
169
- end
170
- end
171
- end
172
- end