Class: Effective::CodeWriter

Inherits:
Object
  • Object
show all
Defined in:
app/models/effective/code_writer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filename, indent: ' ', newline: "\n", &block) ⇒ CodeWriter

Returns a new instance of CodeWriter.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'app/models/effective/code_writer.rb', line 14

def initialize(filename, indent: '  ', newline: "\n", &block)
  @filename = filename
  @indent = indent
  @newline = newline

  @from = []
  @to = []

  @changed = false

  @lines = File.open(filename).readlines

  if block_given?
    block.call(self)
    write!
  end
end

Instance Attribute Details

#filenameObject (readonly)

Returns the value of attribute filename.



12
13
14
# File 'app/models/effective/code_writer.rb', line 12

def filename
  @filename
end

#indentObject (readonly)

Returns the value of attribute indent.



12
13
14
# File 'app/models/effective/code_writer.rb', line 12

def indent
  @indent
end

#linesObject (readonly)

Returns the value of attribute lines.



11
12
13
# File 'app/models/effective/code_writer.rb', line 11

def lines
  @lines
end

#newlineObject (readonly)

Returns the value of attribute newline.



12
13
14
# File 'app/models/effective/code_writer.rb', line 12

def newline
  @newline
end

Instance Method Details

#all(from: @from.last, to: @to.last, &block) ⇒ Object Also known as: select

Returns an array of indexes for each line where the passed block returns true



179
180
181
182
183
184
185
186
187
188
189
# File 'app/models/effective/code_writer.rb', line 179

def all(from: @from.last, to: @to.last, &block)
  retval = []

  each_with_depth do |line, depth, index|
    next if index < (from || 0)
    retval << index if block.call(line, depth, index)
    break if to == index
  end

  retval
end

#changed?Boolean

Returns:

  • (Boolean)


223
224
225
# File 'app/models/effective/code_writer.rb', line 223

def changed?
  @changed == true
end

#depth_at(line_index) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'app/models/effective/code_writer.rb', line 207

def depth_at(line_index)
  if filename.end_with?('.haml')
    return (lines[line_index].length - lines[line_index].lstrip.length) / indent.length
  end

  depth = 0

  Array(lines).each_with_index do |line, index|
    depth -= 1 if close?(line)
    break if line_index == index
    depth += 1 if open?(line)
  end

  depth
end

#each_with_depth(&block) ⇒ Object

Iterate over the lines with a depth, and passed the stripped line to the passed block



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'app/models/effective/code_writer.rb', line 139

def each_with_depth(&block)
  depth = 0
  from_depth = (@from.last ? depth_at(@from.last) : 0)

  Array(lines).each_with_index do |line, index|
    stripped = line.to_s.strip

    depth -= 1 if close?(stripped)

    block.call(stripped, depth - from_depth, index)
    depth += 1 if open?(stripped)
  end

  nil
end

#first(from: @from.last, to: @to.last, &block) ⇒ Object Also known as: find

Returns the index of the first line where the passed block returns true



156
157
158
159
160
161
162
# File 'app/models/effective/code_writer.rb', line 156

def first(from: @from.last, to: @to.last, &block)
  each_with_depth do |line, depth, index|
    next if index < (from || 0)
    return index if block.call(line, depth, index)
    break if to == index
  end
end

#gsub!(source, target) ⇒ Object



227
228
229
# File 'app/models/effective/code_writer.rb', line 227

def gsub!(source, target)
  lines.each { |line| @changed = true if line.gsub!(source, target) }
end

#insert(content, index, depth: nil, content_depth: nil) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'app/models/effective/code_writer.rb', line 80

def insert(content, index, depth: nil, content_depth: nil)
  contents = (content.kind_of?(Array) ? content : content.split(newline)).map { |str| str.strip }

  depth ||= depth_at(index)

  # If the line we're inserting at is a block, fast-forward the end of the block. And add a newline.
  if open?(index)
    index = first(from: index) { |line| close?(line) } + 1
    lines.insert(index, newline)
  elsif !whitespace?(index) && (open?(contents) || !same?(contents, index))
    index += 1
    lines.insert(index, newline)
  end

  content_depth ||= 0

  index = index + 1 # Insert after the given line

  contents.each do |content|
    content_depth -= 1 if close?(content)

    if content == ''
      lines.insert(index, newline)
    else
      lines.insert(index, (indent * (depth + content_depth)) + content + newline)
    end

    index += 1
    content_depth += 1 if open?(content)
  end

  unless whitespace?(index) || close?(index)
    if block?(contents) || !same?(contents, index)
      lines.insert(index, newline)
    end
  end

  @changed = true
end

#insert_after_first(content, depth: nil, content_depth: nil, &block) ⇒ Object

Returns true if the insert happened, nil if no insert



41
42
43
44
45
46
# File 'app/models/effective/code_writer.rb', line 41

def insert_after_first(content, depth: nil, content_depth: nil, &block)
  index = first(&block)
  return nil unless index

  insert(content, index, depth: depth, content_depth: content_depth)
end

#insert_after_last(content, depth: nil, content_depth: nil, &block) ⇒ Object

Returns true if the insert happened, nil if no insert



49
50
51
52
53
54
# File 'app/models/effective/code_writer.rb', line 49

def insert_after_last(content, depth: nil, content_depth: nil, &block)
  index = last(&block)
  return nil unless index

  insert(content, index, depth: depth, content_depth: content_depth)
end

#insert_before_last(content, depth: nil, content_depth: nil, &block) ⇒ Object

Returns true if the insert happened, nil if no insert



57
58
59
60
61
62
# File 'app/models/effective/code_writer.rb', line 57

def insert_before_last(content, depth: nil, content_depth: nil, &block)
  index = last(&block)
  return nil unless index

  insert(content, index-1, depth: depth, content_depth: content_depth)
end

#insert_into_first(content, &block) ⇒ Object

Returns true if the insert happened, nil if no insert



33
34
35
36
37
38
# File 'app/models/effective/code_writer.rb', line 33

def insert_into_first(content, &block)
  index = first(&block)
  return nil unless index

  insert_raw(content, index, depth_at(index) + 1)
end

#insert_raw(content, index, depth = 0) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'app/models/effective/code_writer.rb', line 120

def insert_raw(content, index, depth = 0)
  contents = (content.kind_of?(Array) ? content : content.split(newline))

  index = index + 1 # Insert after the given line

  contents.each do |content|
    if content.strip == ''
      lines.insert(index, newline)
    else
      lines.insert(index, (indent * depth) + content + newline)
    end

    index += 1
  end

  @changed = true
end

#last(from: @from.last, to: @to.last, &block) ⇒ Object

Returns the index of the last line where the passed block returns true



166
167
168
169
170
171
172
173
174
175
176
# File 'app/models/effective/code_writer.rb', line 166

def last(from: @from.last, to: @to.last, &block)
  retval = nil

  each_with_depth do |line, depth, index|
    next if index < (from || 0)
    retval = index if block.call(line, depth, index)
    break if to == index
  end

  retval
end

#map(from: @from.last, to: @to.last, indexes: [], &block) ⇒ Object

Yields each line to a block and returns an array of the results



193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'app/models/effective/code_writer.rb', line 193

def map(from: @from.last, to: @to.last, indexes: [], &block)
  retval = []

  each_with_depth do |line, depth, index|
    next if index < (from || 0)
    next unless indexes.blank? || indexes.include?(index)

    retval << block.call(line, depth, index)
    break if to == index
  end

  retval
end

#remove(from:, to:) ⇒ Object



231
232
233
234
235
# File 'app/models/effective/code_writer.rb', line 231

def remove(from:, to:)
  raise('expected from to be less than to') unless from.present? && to.present? && (from < to)
  @changed = true
  (to - from).times { lines.delete_at(from) }
end

#replace(index, content) ⇒ Object



237
238
239
240
# File 'app/models/effective/code_writer.rb', line 237

def replace(index, content)
  @changed = true
  lines[index].replace(content.to_s)
end

#within(content, &block) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'app/models/effective/code_writer.rb', line 64

def within(content, &block)
  content ||= 0

  from = content.kind_of?(Integer) ? content : first { |line| line.start_with?(content) && open?(line) }
  return nil unless from

  from_depth = depth_at(from)

  to = first(from: from) { |line, depth| depth == from_depth && close?(line) }
  return nil unless to

  @from.push(from); @to.push(to)
  yield
  @from.pop; @to.pop
end

#write!Object



242
243
244
245
246
247
248
249
250
# File 'app/models/effective/code_writer.rb', line 242

def write!
  return false unless changed?

  File.open(filename, 'w') do |file|
    lines.each { |line| file.write(line) }
  end

  true
end