crane 0.1.9

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.
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .loadpath
6
+ .project
7
+ .nbproject
8
+ .uplift_config
9
+ .uplift
10
+ .crane
11
+ .crane_config
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies uplift.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,51 @@
1
+ What?
2
+ ====
3
+
4
+ Crane is a Ruby gem for sending files through FTP without the hassle of clicking directory by directory
5
+ and sendind file by file. It gets the bore out of your path :-)
6
+
7
+ Why?
8
+ ====
9
+
10
+ We have projects in servers that, for particular reasons, can't access Github or a git repo, so
11
+ Capistrano can't be used for deploying.
12
+
13
+ Most of them are small clients that demand basic designs, no
14
+ dynamic programming, usually on shared hosting. After tiny changes, HTML, CSS and image
15
+ files are sent via FTP. This is very boring.
16
+
17
+ How?
18
+ ====
19
+
20
+ Crane analyses files in the current directory by date. Let's say you type:
21
+
22
+ <pre>
23
+ $ crane push today
24
+ </pre>
25
+
26
+ Crane will send all files changed *today* to the server. Automatically.
27
+
28
+ Better yet, try this:
29
+
30
+ <pre>
31
+ $ crane push 1h
32
+ </pre>
33
+
34
+ This will send all files modified in the last hour.
35
+
36
+ How to start
37
+ ====
38
+
39
+ Get into your project root directory and type:
40
+
41
+ <pre>
42
+ $ crane init
43
+ </pre>
44
+
45
+ This will inquire you about FTP informations. This configuration will be saved in a file
46
+ called .crane in the same folder.
47
+
48
+ License
49
+ ====
50
+
51
+ MIT. Do whatever you want. Want a suggestion? Fork and collaborate.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ # just 'rake test'
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "lib"
8
+ t.test_files = Dir["test/**/test*.rb"]
9
+ end
data/bin/crane ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $:.unshift File.expand_path('../../lib/', __FILE__)
4
+ require "crane/shell_initializer"
5
+
6
+ shell = Shell::Initializer.new(ARGV)
data/crane.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "crane/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "crane"
7
+ s.version = Crane::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Alexandre de Oliveira"]
10
+ s.email = ["chavedomundo@gmail.com"]
11
+ s.homepage = "http://github.com/kurko/crane"
12
+ s.summary = %q{Send files via FTP automatically.}
13
+ s.description = %q{This gem allows you to easily send files from your project to a remote
14
+ server via FTP. Designers and interface programmers can have great benefit
15
+ from this, for they can easily send all last modified files without needing
16
+ an specific application for that nor searching directories by hand.}
17
+
18
+ s.rubyforge_project = "crane"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
data/lib/crane.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Crane
2
+
3
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path("../../crane.rb", __FILE__)
2
+ require File.expand_path("../../config.rb", __FILE__)
3
+ require 'crane/ftp'
4
+
5
+ module Crane
6
+ module Commands
7
+ class Init < Crane::Engine
8
+
9
+ def run
10
+ @config = Config.load_config
11
+ ask_ftp_info
12
+ Config.save_config @config
13
+ end
14
+
15
+ def ask_ftp_info
16
+ ftp = {}
17
+
18
+ print "Your FTP host address (e.g. ftp.mysite.com.br): "
19
+ ftp[:host] = Shell::Input.text
20
+
21
+ print "FTP username: "
22
+ ftp[:username] = Shell::Input.text
23
+
24
+ system "stty -echo"
25
+ print "FTP password: "
26
+ ftp[:password] = Shell::Input.text
27
+ system "stty echo"
28
+ print "\n"
29
+
30
+ print "Type the path to the site's folder (e.g. /public_html ): "
31
+ ftp[:remote_root_dir] = Shell::Input.text
32
+
33
+ @config[:ftp] = ftp
34
+
35
+ end
36
+
37
+ def help
38
+ puts "Initializes environment."
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,151 @@
1
+ require File.expand_path("../../crane.rb", __FILE__)
2
+ require File.expand_path("../../config.rb", __FILE__)
3
+ require 'crane/ftp'
4
+
5
+ module Crane
6
+ module Commands
7
+
8
+ class Push < Crane::Engine
9
+
10
+ @ignored_files = []
11
+
12
+ def run
13
+ time_frame = Shell::Parser.get_arguments(@argv).first
14
+
15
+ (puts "No config file found. Run 'crane init' to create one."; exit) unless Config.has_config_file?
16
+
17
+ @local_files = get_files time_frame
18
+
19
+ if @local_files.length == 0
20
+ puts "No files were found."
21
+ exit
22
+ elsif @local_files.length == 1
23
+ do_push_files = Shell::Input.yesno "Push 1 file to the server? [Y/n]"
24
+ else
25
+ do_push_files = Shell::Input.yesno "Push "+@local_files.length.to_s+" files to the server? [Y/n]"
26
+ end
27
+
28
+ exit unless do_push_files
29
+
30
+ print "\nConnecting to host... "
31
+
32
+ ftp = Crane::Ftp.new
33
+ if ftp == false then
34
+ puts "ops, an error ocurred."
35
+ exit
36
+ else
37
+ puts "connected. Started sending files..."
38
+ end
39
+
40
+ push_files ftp
41
+ end
42
+
43
+ def push_files ftp
44
+
45
+ prefix_dir = @config[:ftp][:remote_root_dir]
46
+ prefix_dir << "/" unless prefix_dir[ prefix_dir.length-1,1 ] == "/"
47
+
48
+ @local_files.each { |f|
49
+ st_mk = Time.new
50
+
51
+ dir = prefix_dir
52
+ unless [".", ".."].include? File.dirname(f)
53
+ dir = prefix_dir + File.dirname(f)
54
+ ftp.mkdir dir
55
+ end
56
+
57
+ st_end = Time.new
58
+ t = st_end-st_mk
59
+ print "."
60
+
61
+ pwd = ftp.connection.pwd
62
+ ftp.chdir dir
63
+ st_put = Time.new
64
+
65
+ begin
66
+ result = ftp.putbinaryfile f, File.basename(f)
67
+ rescue
68
+
69
+ end
70
+ st_putend = Time.new
71
+ t = st_putend-st_put
72
+ print "."
73
+
74
+ STDOUT.flush
75
+ ftp.connection.chdir pwd
76
+ }
77
+ puts "Done!"
78
+ end
79
+
80
+ # if user defines a time interval for the file, check if it complies
81
+ def within_defined_interval? file, time_frame = ""
82
+ time = Time.new
83
+ file_time = Time.at File.stat(file).mtime
84
+ yesterday = time - (60 * 60 * 24)
85
+
86
+ if time_frame == "today"
87
+ return true if file_time.year+file_time.month+file_time.day == time.year+time.month+time.day
88
+ elsif time_frame == "yesterday"
89
+ return true if file_time.year+file_time.month+file_time.day == yesterday.year+yesterday.month+yesterday.day
90
+ elsif time_frame =~ /^[1-9]h$/
91
+ seconds = time_frame[/[1-9]/].to_i * (60 * 60)
92
+ return true if file_time > (time - seconds)
93
+ elsif time_frame == "all"
94
+ return true
95
+ elsif time_frame == ""
96
+ true
97
+ end
98
+ false
99
+ end
100
+
101
+ def get_files time_frame = "", search_folder = ""
102
+
103
+ @ignored_files = get_ignored_files
104
+ local_files = []
105
+
106
+ if search_folder == ""
107
+ search_folder = "*"
108
+ else
109
+ search_folder << "*"
110
+ end
111
+
112
+ Dir.glob(search_folder, File::FNM_DOTMATCH).each { |file|
113
+ filename = File.basename file
114
+
115
+ next if [".", ".."].include? filename
116
+ next if @ignored_files.include? filename
117
+ next if @ignored_files.include? file
118
+
119
+ if File.stat(file).directory? then
120
+ local_files += get_files(time_frame, file+"/").flatten
121
+ elsif within_defined_interval? file, time_frame
122
+ puts file if is_option("list") || is_option("l")
123
+ local_files.push file
124
+ end
125
+ }
126
+ local_files
127
+ end
128
+
129
+ def help
130
+ print "Usage:\n"
131
+ print "\s\scrane push [time_frame] [options]"
132
+ print "\n\n"
133
+ print "Time frame:"
134
+ print "\n"
135
+ print "\s\s1h\t\tAll files modified since 1h hour ago.\n"
136
+ print "\s\sxh\t\tAll files modified since xh hours ago (subtitute x by a number).\n"
137
+ print "\s\stoday\t\tAll files modified today.\n"
138
+ print "\s\syesterday\tAll files modified yesterday.\n"
139
+ print "\n"
140
+ print "If no time frame is set, Crane will parse all files."
141
+ print "\n\n"
142
+ print "Options:"
143
+ print "\n"
144
+ print "\s\s-l, --list\tList all files found in the set time frame."
145
+ print "\s\s-h, --help\tShow this help."
146
+ print "\n"
147
+ end
148
+
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path("../../crane.rb", __FILE__)
2
+ require File.expand_path("../../config.rb", __FILE__)
3
+ require 'crane/ftp'
4
+
5
+ module Crane
6
+ module Commands
7
+ class Test < Crane::Engine
8
+
9
+ include Config
10
+
11
+ def run
12
+ puts "Starting tests:"
13
+
14
+ # has configuration file?
15
+ if Config.has_config_file?
16
+ puts "\tConfiguration file: ok."
17
+ else
18
+ puts "\tConfiguration file: failed."
19
+ exit
20
+ end
21
+
22
+ # has connection?
23
+ if has_ftp_connection?
24
+ puts "\tFTP connection: ok."
25
+ else
26
+ puts "\tFTP connection: failed"
27
+ exit
28
+ end
29
+
30
+ # can change dir?
31
+ if ftp_remote_dir?
32
+ puts "\tFTP remote dir: ok."
33
+ else
34
+ puts "\tFTP remote dir: failed => inexistent remote dir"
35
+ end
36
+
37
+ puts "Everything's ok." if @errors == false
38
+ end
39
+
40
+ def has_ftp_connection?
41
+ @ftp = Crane::Ftp.new
42
+ return false if @ftp.connection.nil?
43
+ if @ftp.connection.respond_to?("closed?")
44
+ !@ftp.connection.closed?
45
+ end
46
+ end
47
+
48
+ def ftp_remote_dir?
49
+ @ftp = Crane::Ftp.new if @ftp.nil?
50
+ @ftp.remote_dir_exists?
51
+ end
52
+
53
+ def help
54
+ puts "Tests environment."
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,110 @@
1
+ module Config
2
+
3
+ @CONFIG = {}
4
+
5
+ @PATH = "./.crane"
6
+
7
+ @IGNORE_FILES = [
8
+ ".DS_Store",
9
+ ".crane",
10
+ ".project",
11
+ "nb_project",
12
+ ".loadpath",
13
+ ".gitignore",
14
+ ".git",
15
+ ".gitmodules"
16
+ ]
17
+
18
+ class << self
19
+ attr_accessor :PATH, :FILENAME, :IGNORE_FILES, :CONFIG
20
+ end
21
+
22
+ def self.get_ignored_files
23
+ if File.exists? ".gitignore"
24
+ IO.readlines(".gitignore").each { |e|
25
+ @IGNORE_FILES.push e.gsub(/\n/, "")
26
+ }
27
+ end
28
+ @IGNORE_FILES
29
+ end
30
+
31
+ def self.has_config_file?
32
+ File.exists? @PATH
33
+ end
34
+
35
+ def self.make_config config
36
+ data = ""
37
+
38
+ config.each { |key, value|
39
+ # a new section
40
+ if value.class.to_s == "Hash" then
41
+ data << "[" + key.to_s + "]" + "\n"
42
+ data << self.make_config(value).to_s
43
+ elsif ["String", "Symbol"].include? value.class.to_s
44
+ data << key.to_s + " = " +value.to_s + "\n"
45
+ end
46
+ }
47
+
48
+ data
49
+ end
50
+
51
+ def self.save_config config
52
+ @CONFIG = config
53
+ data = self.make_config config
54
+ unless data.empty?
55
+ # deletes configuration file to recreate
56
+ File.delete self.PATH if File.exists? self.PATH
57
+
58
+ File.open(self.PATH, "w+") { |cfile|
59
+ File.chmod(0777, self.PATH)
60
+ data.each_line { |l|
61
+ cfile.puts l
62
+ }
63
+ }
64
+ return true
65
+ end
66
+ false
67
+ end
68
+
69
+ def self.load_config
70
+ config = {}
71
+
72
+ if File.exists? Config.PATH
73
+ cfile = File.open Config.PATH, 'r'
74
+
75
+ section = ""
76
+
77
+ all_properties = {}
78
+ cfile.each { |l|
79
+ maybe_section = l.scan(/\[(.*)\]\n/)
80
+ maybe_property = l.scan(/(.*)=(.*)\n/)
81
+
82
+ maybe_section = maybe_section[0][0] if maybe_section[0]
83
+
84
+ unless maybe_section.empty? then
85
+ unless (section.empty? and all_properties.empty? )
86
+ config[section.to_sym] = all_properties
87
+ all_properties = ""
88
+ end
89
+ section = maybe_section
90
+ else maybe_property.empty?
91
+ l.scan(/(.*) = (.*)\n/) {
92
+ |property, value|
93
+ all_properties[property.to_sym] = value.to_s
94
+ }
95
+ end
96
+
97
+ }
98
+
99
+ unless (section.empty? and all_properties.empty? )
100
+ config[section.to_sym] = all_properties
101
+ all_properties = ""
102
+ end
103
+
104
+ cfile.close
105
+ end
106
+ @CONFIG = config
107
+ config
108
+ end
109
+
110
+ end