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: ' '.freeze, newline: "\n".freeze, &block) ⇒ CodeWriter

Returns a new instance of CodeWriter.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'app/models/effective/code_writer.rb', line 7

def initialize(filename, indent: '  '.freeze, newline: "\n".freeze, &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

#filename ⇒ Object (readonly)

Returns the value of attribute filename.



5
6
7
# File 'app/models/effective/code_writer.rb', line 5

def filename
  @filename
end

#indent ⇒ Object (readonly)

Returns the value of attribute indent.



5
6
7
# File 'app/models/effective/code_writer.rb', line 5

def indent
  @indent
end

#lines ⇒ Object (readonly)

Returns the value of attribute lines.



4
5
6
# File 'app/models/effective/code_writer.rb', line 4

def lines
  @lines
end

#newline ⇒ Object (readonly)

Returns the value of attribute newline.



5
6
7
# File 'app/models/effective/code_writer.rb', line 5

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



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

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)


200
201
202
# File 'app/models/effective/code_writer.rb', line 200

def changed?
  @changed == true
end

#depth_at(line_index) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'app/models/effective/code_writer.rb', line 184

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



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'app/models/effective/code_writer.rb', line 116

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



133
134
135
136
137
138
139
# File 'app/models/effective/code_writer.rb', line 133

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



204
205
206
# File 'app/models/effective/code_writer.rb', line 204

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

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



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'app/models/effective/code_writer.rb', line 57

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_last(content, depth: nil, content_depth: nil, &block) ⇒ Object

Returns true if the insert happened, nil if no insert



26
27
28
29
30
31
# File 'app/models/effective/code_writer.rb', line 26

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



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

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_raw(content, index, depth = 0) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'app/models/effective/code_writer.rb', line 97

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



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

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



170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/models/effective/code_writer.rb', line 170

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

#replace(index, content) ⇒ Object



208
209
210
211
# File 'app/models/effective/code_writer.rb', line 208

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

#within(content, &block) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/models/effective/code_writer.rb', line 41

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