Module: Ing::Files

Included in:
Generator
Defined in:
lib/ing/files.rb,
lib/ing/actions/directory.rb,
lib/ing/actions/create_file.rb,
lib/ing/actions/create_link.rb,
lib/ing/actions/empty_directory.rb,
lib/ing/actions/inject_into_file.rb,
lib/ing/actions/file_manipulation.rb

Overview

Provides filesystem actions using the same interface as Thor::Actions

The target class must provide at least:

attr_reader :source_root, :destination_root
attr_reader :shell, :options

and should provide these options (otherwise all defaulted nil): verbose, force, pretend, revoke, quiet, skip.

Defined Under Namespace

Classes: CreateFile, CreateLink, Directory, EmptyDirectory, InjectIntoFile

Instance Method Summary collapse

Instance Method Details

#action(instance) ⇒ Object

Wraps an action object and call it accordingly to the behavior attribute.



79
80
81
82
83
84
85
# File 'lib/ing/files.rb', line 79

def action(instance) #:nodoc:
  if revoke?
    instance.revoke!
  else
    instance.invoke!
  end
end

#append_to_file(path, *args, &block) ⇒ Object Also known as: append_file

Append text to a file. Since it depends on insert_into_file, it’s reversible.

Parameters

path<String>

path of the file to be changed

data<String>

the data to append to the file, can be also given as a block.

config<Hash>

give :verbose => false to not log the status.

Example

append_to_file 'config/environments/test.rb', 'config.gem "rspec"'

append_to_file 'config/environments/test.rb' do
  'config.gem "rspec"'
end


172
173
174
175
176
# File 'lib/ing/actions/file_manipulation.rb', line 172

def append_to_file(path, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config.merge!(:before => /\z/)
  insert_into_file(path, *(args << config), &block)
end

#chmod(path, mode, config = {}) ⇒ Object

Changes the mode of the given file or directory.

Parameters

mode<Integer>

the file mode

path<String>

the name of the file to change mode

config<Hash>

give :verbose => false to not log the status.

Example

chmod "script/*", 0755


128
129
130
131
132
133
# File 'lib/ing/actions/file_manipulation.rb', line 128

def chmod(path, mode, config={})
  return if revoke?
  path = File.expand_path(path, destination_root)
  shell.say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
  FileUtils.chmod_R(mode, path) unless options[:pretend]
end

#comment_lines(path, flag, *args) ⇒ Object

Comment all lines matching a given regex. It will leave the space which existed before the beginning of the line in tact and will insert a single space after the comment hash.

Parameters

path<String>

path of the file to be changed

flag<Regexp|String>

the regexp or string used to decide which lines to comment

config<Hash>

give :verbose => false to not log the status.

Example

comment_lines 'config/initializers/session_store.rb', /cookie_store/


264
265
266
267
268
# File 'lib/ing/actions/file_manipulation.rb', line 264

def comment_lines(path, flag, *args)
  flag = flag.respond_to?(:source) ? flag.source : flag

  gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args)
end

#copy_file(source, *args, &block) ⇒ Object

Copies the file from the relative source to the relative destination. If the destination is not given it’s assumed to be equal to the source.

Parameters

source<String>

the relative path to the source root.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status.

Examples

copy_file "README", "doc/README"

copy_file "doc/README"


21
22
23
24
25
26
27
28
29
30
31
# File 'lib/ing/actions/file_manipulation.rb', line 21

def copy_file(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  source = File.expand_path(find_in_source_paths(source.to_s))

  create_file destination, nil, config do
    content = File.binread(source)
    content = block.call(content) if block
    content
  end
end

#create_file(destination, *args, &block) ⇒ Object Also known as: add_file

Create a new file relative to the destination root with the given data, which is the return value of a block or a data string.

Parameters

destination<String>

the relative path to the destination root.

data<String|NilClass>

the data to append to the file.

config<Hash>

give :verbose => false to not log the status.

Examples

create_file "lib/fun_party.rb" do
  hostname = ask("What is the virtual hostname I should use?")
  "vhost.name = #{hostname}"
end

create_file "config/apache.conf", "your apache config"


23
24
25
26
27
# File 'lib/ing/actions/create_file.rb', line 23

def create_file(destination, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  data = args.first
  action CreateFile.new(self, destination, block || data.to_s, config)
end

Create a new file relative to the destination root from the given source.

Parameters

destination<String>

the relative path to the destination root.

source<String|NilClass>

the relative path to the source root.

config<Hash>

give :verbose => false to not log the status.

give :symbolic => false for hard link.

Examples

create_link "config/apache.conf", "/etc/apache.conf"


18
19
20
21
22
# File 'lib/ing/actions/create_link.rb', line 18

def create_link(destination, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  source = args.first
  action CreateLink.new(self, destination, source, config)
end

#current_destinationObject



73
74
75
# File 'lib/ing/files.rb', line 73

def current_destination
  destination_stack.last
end

#directory(source, *args, &block) ⇒ Object

Copies recursively the files from source directory to root directory. If any of the files finishes with .tt, it’s considered to be a template and is placed in the destination without the extension .tt. If any empty directory is found, it’s copied and all .empty_directory files are ignored. If any file name is wrapped within % signs, the text within the % signs will be executed as a method and replaced with the returned value. Let’s suppose a doc directory with the following files:

doc/
  components/.empty_directory
  README
  rdoc.rb.tt
  %app_name%.rb

When invoked as:

directory "doc"

It will create a doc directory in the destination with the following files (assuming that the ‘app_name` method returns the value “blog”):

doc/
  components/
  README
  rdoc.rb
  blog.rb

Encoded path note: Since Thor internals use Object#respond_to? to check if it can expand %something%, this ‘something` should be a public method in the class calling #directory. If a method is private, Thor stack raises PrivateMethodEncodedError.

Parameters

source<String>

the relative path to the source root.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status. If :recursive => false, does not look for paths recursively.

Examples

directory "doc"
directory "doc", "docs", :recursive => false


47
48
49
50
51
# File 'lib/ing/actions/directory.rb', line 47

def directory(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  action Directory.new(self, source, destination || source, config, &block)
end

#empty_directory(destination, config = {}) ⇒ Object

Creates an empty directory.

Parameters

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status.

Examples

empty_directory "doc"


14
15
16
# File 'lib/ing/actions/empty_directory.rb', line 14

def empty_directory(destination, config={})
  action EmptyDirectory.new(self, destination, config)
end

#find_in_source_paths(file) ⇒ Object

Receives a file or directory and search for it in the source paths.

Note that at minimum, the base object must define source_root. If source_paths is also defined, those will be used to search for files first.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/ing/files.rb', line 106

def find_in_source_paths(file)
  relative_root = relative_to_original_destination_root(current_destination, false)
  source_paths  = (respond_to?(:source_paths) ? self.source_paths : []) +
                  [self.source_root]
                  
  source_paths.each do |source|
    source_file = File.expand_path(file, File.join(source, relative_root))
    return source_file if File.exists?(source_file)
  end

  message = "Could not find #{file.inspect} in any of your source paths. "

  unless source_root
    message << "Please set the source_root with the path containing your templates."
  end

  if source_paths.empty?
    message << "Currently you have no source paths."
  else
    message << "Your current source paths are: \n#{source_paths.join("\n")}"
  end

  raise Ing::FileNotFoundError, message
end

#force?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/ing/files.rb', line 52

def force?
  !!options[:force]
end

#get(source, *args, &block) ⇒ Object

Gets the content at the given address and places it at the given relative destination. If a block is given instead of destination, the content of the url is yielded and used as location.

Parameters

source<String>

the address of the given content.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status.

Examples

get "http://gist.github.com/103208", "doc/README"

get "http://gist.github.com/103208" do |content|
  content.split("\n").first
end


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/ing/actions/file_manipulation.rb', line 72

def get(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first

  source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^https?\:\/\//
  render = open(source) {|input| input.binmode.read }

  destination ||= if block_given?
    block.arity == 1 ? block.call(render) : block.call
  else
    File.basename(source)
  end

  create_file destination, render, config
end

#gsub_file(path, flag, *args, &block) ⇒ Object

Run a regular expression replacement on a file.

Parameters

path<String>

path of the file to be changed

flag<Regexp|String>

the regexp or string to be replaced

replacement<String>

the replacement, can be also given as a block

config<Hash>

give :verbose => false to not log the status.

Example

gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'

gsub_file 'README', /rake/, :green do |match|
  match << " no more. Use thor!"
end


218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/ing/actions/file_manipulation.rb', line 218

def gsub_file(path, flag, *args, &block)
  return if revoke?
  config = args.last.is_a?(Hash) ? args.pop : {}

  path = File.expand_path(path, destination_root)
  shell.say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)

  unless options[:pretend]
    content = File.binread(path)
    content.gsub!(flag, *args, &block)
    File.open(path, 'wb') { |file| file.write(content) }
  end
end

#in_rootObject

Goes to the current root and execute the given block.



168
169
170
# File 'lib/ing/files.rb', line 168

def in_root
  inside(destination_root) { yield }
end

#inject_into_class(path, klass, *args, &block) ⇒ Object

Injects text right after the class definition. Since it depends on insert_into_file, it’s reversible.

Parameters

path<String>

path of the file to be changed

klass<String|Class>

the class to be manipulated

data<String>

the data to append to the class, can be also given as a block.

config<Hash>

give :verbose => false to not log the status.

Examples

inject_into_class "app/controllers/application_controller.rb", ApplicationController, "  filter_parameter :password\n"

inject_into_class "app/controllers/application_controller.rb", ApplicationController do
  "  filter_parameter :password\n"
end


196
197
198
199
200
# File 'lib/ing/actions/file_manipulation.rb', line 196

def inject_into_class(path, klass, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/)
  insert_into_file(path, *(args << config), &block)
end

#insert_into_file(destination, *args, &block) ⇒ Object Also known as: inject_into_file

Injects the given content into a file. Different from gsub_file, this method is reversible.

Parameters

destination<String>

Relative path to the destination root

data<String>

Data to add to the file. Can be given as a block.

config<Hash>

give :verbose => false to not log the status and the flag for injection (:after or :before) or :force => true for insert two or more times the same content.

Examples

insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"

insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
  gems = ask "Which gems would you like to add?"
  gems.split(" ").map{ |gem| "  config.gem :#{gem}" }.join("\n")
end


25
26
27
28
29
30
31
32
# File 'lib/ing/actions/inject_into_file.rb', line 25

def insert_into_file(destination, *args, &block)
  if block_given?
    data, config = block, args.shift
  else
    data, config = args.shift, args.shift
  end
  action InjectIntoFile.new(self, destination, data, config)
end

#inside(dir = '', config = {}, &block) ⇒ Object

Do something in the root or on a provided subfolder. If a relative path is given it’s referenced from the current root. The full path is yielded to the block you provide. The path is set back to the previous path when the method exits.

Parameters

dir<String>

the directory to move to.

config<Hash>

give :verbose => true to log and use padding.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/ing/files.rb', line 141

def inside(dir='', config={}, &block)
  verbose = config.fetch(:verbose, verbose?)

  shell.say_status :inside, dir, verbose
  shell.padding += 1 if verbose
  destination_stack.push File.expand_path(dir, current_destination)

  # If the directory doesnt exist and we're not pretending
  if !File.exist?(current_destination)
    FileUtils.mkdir_p(current_destination, :noop => pretend?)
  end

  if pretend?
    # In pretend mode, just yield down to the block
    block.arity == 1 ? yield(current_destination) : yield
  else
    FileUtils.cd(current_destination) do
      block.arity == 1 ? yield(current_destination) : yield 
    end
  end
  
  destination_stack.pop
  shell.padding -= 1 if verbose
end

Links the file from the relative source to the relative destination. If the destination is not given it’s assumed to be equal to the source.

Parameters

source<String>

the relative path to the source root.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status.

Examples

link_file "README", "doc/README"

link_file "doc/README"


47
48
49
50
51
52
53
# File 'lib/ing/actions/file_manipulation.rb', line 47

def link_file(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source
  source = File.expand_path(find_in_source_paths(source.to_s))

  create_link destination, source, config
end

#prepend_to_file(path, *args, &block) ⇒ Object Also known as: prepend_file

Prepend text to a file. Since it depends on insert_into_file, it’s reversible.

Parameters

path<String>

path of the file to be changed

data<String>

the data to prepend to the file, can be also given as a block.

config<Hash>

give :verbose => false to not log the status.

Example

prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"'

prepend_to_file 'config/environments/test.rb' do
  'config.gem "rspec"'
end


150
151
152
153
154
# File 'lib/ing/actions/file_manipulation.rb', line 150

def prepend_to_file(path, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  config.merge!(:after => /\A/)
  insert_into_file(path, *(args << config), &block)
end

#pretend?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/ing/files.rb', line 48

def pretend?
  !!options[:pretend]
end

#quiet?Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/ing/files.rb', line 60

def quiet?
  !!options[:quiet]
end

#relative_to_original_destination_root(path, remove_dot = true) ⇒ Object

Returns the given path relative to the absolute root (ie, root where the script started).



90
91
92
93
94
95
96
97
# File 'lib/ing/files.rb', line 90

def relative_to_original_destination_root(path, remove_dot=true)
  path = path.dup
  if path.gsub!(destination_root, '.')
    remove_dot ? (path[2..-1] || '') : path
  else
    path
  end
end

#remove_file(path, config = {}) ⇒ Object Also known as: remove_dir

Removes a file at the given location.

Parameters

path<String>

path of the file to be changed

config<Hash>

give :verbose => false to not log the status.

Example

remove_file 'README'
remove_file 'app/controllers/application_controller.rb'


281
282
283
284
285
286
287
# File 'lib/ing/actions/file_manipulation.rb', line 281

def remove_file(path, config={})
  return if revoke?
  path  = File.expand_path(path, destination_root)

  shell.say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
  ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path)
end

#revoke?Boolean

Returns:

  • (Boolean)


64
65
66
# File 'lib/ing/files.rb', line 64

def revoke?
  !!options[:revoke]
end

#skip?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/ing/files.rb', line 68

def skip?
  !!options[:skip]
end

#template(source, *args, &block) ⇒ Object

Gets an ERB template at the relative source, executes it and makes a copy at the relative destination. If the destination is not given it’s assumed to be equal to the source removing .tt from the filename.

Parameters

source<String>

the relative path to the source root.

destination<String>

the relative path to the destination root.

config<Hash>

give :verbose => false to not log the status.

Examples

template "README", "doc/README"

template "doc/README"


103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/ing/actions/file_manipulation.rb', line 103

def template(source, *args, &block)
  config = args.last.is_a?(Hash) ? args.pop : {}
  destination = args.first || source.sub(/\.tt$/, '')

  source  = File.expand_path(find_in_source_paths(source.to_s))
  context = instance_eval('binding')

  create_file destination, nil, config do
    content = ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context)
    content = block.call(content) if block
    content
  end
end

#uncomment_lines(path, flag, *args) ⇒ Object

Uncomment all lines matching a given regex. It will leave the space which existed before the comment hash in tact but will remove any spacing between the comment hash and the beginning of the line.

Parameters

path<String>

path of the file to be changed

flag<Regexp|String>

the regexp or string used to decide which lines to uncomment

config<Hash>

give :verbose => false to not log the status.

Example

uncomment_lines 'config/initializers/session_store.rb', /active_record/


245
246
247
248
249
# File 'lib/ing/actions/file_manipulation.rb', line 245

def uncomment_lines(path, flag, *args)
  flag = flag.respond_to?(:source) ? flag.source : flag

  gsub_file(path, /^(\s*)#\s*(.*#{flag})/, '\1\2', *args)
end

#verbose?Boolean

Returns:

  • (Boolean)


56
57
58
# File 'lib/ing/files.rb', line 56

def verbose?
  !!options[:verbose]
end