sinatra-chassis 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/LICENSE +7 -0
- data/README.md +19 -0
- data/Rakefile +1 -0
- data/bin/chassis +73 -0
- data/lib/sinatra/chassis.rb +213 -0
- data/lib/sinatra/chassis/file_manager.rb +151 -0
- data/lib/sinatra/chassis/helpers.rb +237 -0
- data/lib/sinatra/chassis/tasks.rb +7 -0
- data/lib/sinatra/tasks/assets.rake +40 -0
- data/lib/sinatra/tasks/chassis.rake +23 -0
- data/lib/sinatra/tasks/datamapper.rake +117 -0
- data/lib/sinatra/tasks/pony.rake +11 -0
- data/lib/sinatra/tasks/sinatra.rake +23 -0
- data/lib/sinatra/templates/chassis/.gitignore +6 -0
- data/lib/sinatra/templates/chassis/Gemfile +8 -0
- data/lib/sinatra/templates/chassis/README.md +4 -0
- data/lib/sinatra/templates/chassis/Rakefile +2 -0
- data/lib/sinatra/templates/chassis/app.rb +20 -0
- data/lib/sinatra/templates/chassis/config.ru +2 -0
- data/lib/sinatra/templates/chassis/public/favicon.ico +0 -0
- data/lib/sinatra/templates/chassis/public/robots.txt +3 -0
- data/lib/sinatra/templates/chassis/tmp/restart.txt +0 -0
- data/lib/sinatra/templates/chassis/views/layout.erb +18 -0
- data/lib/sinatra/templates/chassis/views/readme.erb +23 -0
- data/lib/sinatra/templates/datamapper/data/migrations/datamapper_migration.rb +13 -0
- data/lib/sinatra/templates/datamapper/data/seeds/seed.rb +1 -0
- data/lib/sinatra/templates/datamapper/models/datamapper.rb +8 -0
- data/lib/sinatra/templates/datamapper/settings/datamapper.rb +16 -0
- data/lib/sinatra/templates/datamapper/tests/models/datamapper_tests.rb +16 -0
- data/lib/sinatra/templates/pony/settings/pony.rb +27 -0
- data/lib/sinatra/templates/sinatra/routes/routes.rb +7 -0
- data/lib/sinatra/templates/sinatra/tests/routes/routes_tests.rb +17 -0
- data/lib/sinatra/views/error.erb +36 -0
- data/sinatra-chassis.gemspec +28 -0
- metadata +149 -0
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'rack-flash'
|
3
|
+
|
4
|
+
# Public: Adds helpers
|
5
|
+
module Sinatra
|
6
|
+
|
7
|
+
module ChassisExtraHelpers
|
8
|
+
# Public: Checks if the argument can be evaluated numerically.
|
9
|
+
#
|
10
|
+
# Example
|
11
|
+
#
|
12
|
+
# is_numeric?('1')
|
13
|
+
# # => true
|
14
|
+
#
|
15
|
+
# is_numeric?('hello')
|
16
|
+
# # => false
|
17
|
+
#
|
18
|
+
# Returns true/false.
|
19
|
+
def numeric? x
|
20
|
+
true if Float(x) rescue false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Public: Truncates a String or Number.
|
24
|
+
#
|
25
|
+
# x - String or Number to truncate
|
26
|
+
# word_count - Integer number of words to return if x is a String (default: 100)
|
27
|
+
# end_string - String appended to the end of the returned String if
|
28
|
+
# the original x String is longer than word_count (default: '...')
|
29
|
+
# decimal - Integer number of maximum places after decimal if x is a Number (default: 1)
|
30
|
+
# trailing_zeros - Boolean to decide if a returned Number should keep the
|
31
|
+
# trailing 0's as a String (default: false)
|
32
|
+
#
|
33
|
+
# Examples
|
34
|
+
#
|
35
|
+
# truncate('Lorem ipsum dolor sit amet.', word_count: 3, end_string: '...')
|
36
|
+
# # => 'Lorem ipsum dolor...'
|
37
|
+
#
|
38
|
+
# truncate(1.234000, decimal: 1)
|
39
|
+
# # => 1.2
|
40
|
+
# truncate(1.234000, decimal: 5)
|
41
|
+
# # => 1.234
|
42
|
+
# truncate(1.234000, decimal: 5, trailing_zeros: true)
|
43
|
+
# # => '1.23400'
|
44
|
+
#
|
45
|
+
# Returns a String or Float.
|
46
|
+
def truncate x, options = {}
|
47
|
+
options[:word_count] ||= 100
|
48
|
+
options[:end_string] ||= '...'
|
49
|
+
options[:decimal] ||= 2
|
50
|
+
options[:trailing_zeros] ||= false
|
51
|
+
|
52
|
+
if x.kind_of? String
|
53
|
+
return if x == nil
|
54
|
+
words = x.split()
|
55
|
+
return words[0..(options[:word_count]-1)].join(' ') + (words.length > options[:word_count] ? options[:end_string] : '')
|
56
|
+
elsif is_numeric? x
|
57
|
+
number = "%.#{options[:decimal]}f" % x.to_f
|
58
|
+
number = number.to_f unless options[:trailing_zeros]
|
59
|
+
return number
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Public: Uppercases the first letter of each word.
|
64
|
+
#
|
65
|
+
# Example
|
66
|
+
#
|
67
|
+
# titleize('hello world')
|
68
|
+
# # => 'Hello World'
|
69
|
+
#
|
70
|
+
# Returns a String.
|
71
|
+
def titleize x
|
72
|
+
title = ''
|
73
|
+
x.to_s.split(' ').each { |s| title << s.capitalize + ' ' }
|
74
|
+
title
|
75
|
+
end
|
76
|
+
|
77
|
+
# Public: Compares a string to the request path.
|
78
|
+
#
|
79
|
+
# path - String to compare against the request path
|
80
|
+
#
|
81
|
+
# Exmaple
|
82
|
+
#
|
83
|
+
# Requested URL: http://localhost:4567/hello/world
|
84
|
+
# active('hello')
|
85
|
+
# # => 'active'
|
86
|
+
# active('goodbye')
|
87
|
+
# # => ''
|
88
|
+
#
|
89
|
+
# Returns a Sting: 'active' if there's a match, '' if not.
|
90
|
+
def active path
|
91
|
+
path = Array[path] unless path.kind_of? Array
|
92
|
+
match = false
|
93
|
+
path.each { |p| match = true if request.path_info.include?(p) }
|
94
|
+
'active' if match
|
95
|
+
end
|
96
|
+
|
97
|
+
# Public: Displays an alert unless flash[:alert] if nil.
|
98
|
+
# flash[:alert] empties itself after the first request after it's set.
|
99
|
+
# To use, set flash[:alert] to a String.
|
100
|
+
#
|
101
|
+
# Example
|
102
|
+
# <%= alert %>
|
103
|
+
#
|
104
|
+
# Returns a div with an id of 'alert' and containing the contents
|
105
|
+
# of flash[:alert], or if flash[:alert] if nil, returns nothing.
|
106
|
+
def alert
|
107
|
+
"<div id='alert'>#{flash[:alert]}</div>" if flash[:alert]
|
108
|
+
end
|
109
|
+
|
110
|
+
# Public: Hides an HTML element.
|
111
|
+
# Useful in combination with inline true/false comparators.
|
112
|
+
#
|
113
|
+
# Example
|
114
|
+
#
|
115
|
+
# <div style="<%= hidden unless session[:user] %>">Hello, world!</div>
|
116
|
+
#
|
117
|
+
# Returns a String of CSS.
|
118
|
+
def hidden
|
119
|
+
'display: none;'
|
120
|
+
end
|
121
|
+
|
122
|
+
# Public: Converts a Date to select form fields.
|
123
|
+
# All generated fields have classes of month_select, day_select, or year_select.
|
124
|
+
# None of the options are required, but they are all recommended.
|
125
|
+
#
|
126
|
+
# select_name - name attr for fields, appended with '_day', '_month', or '_year'
|
127
|
+
# select_id - id attr for fields, appended with '_day', '_month', or '_year'
|
128
|
+
# select_class - class attr for fields, appended with '_day', '_month', or '_year'
|
129
|
+
# start_year - first year to use in year select (default: 3 years ago)
|
130
|
+
# end_year - last year to use in year select (default: 3 years from now)
|
131
|
+
# day_first - Boolean to show the day before the month (default: false)
|
132
|
+
# month_name - Boolean to display month names (default: false)
|
133
|
+
#
|
134
|
+
# Example
|
135
|
+
#
|
136
|
+
# date_select(
|
137
|
+
# DateTime.now
|
138
|
+
# select_class: 'your_class',
|
139
|
+
# select_id: 'your_id',
|
140
|
+
# select_name: 'your_name',
|
141
|
+
# start_year: 1999,
|
142
|
+
# end_year: 2021,
|
143
|
+
# day_first: true,
|
144
|
+
# month_name: false
|
145
|
+
# )
|
146
|
+
#
|
147
|
+
# Returns select fields as a String.
|
148
|
+
def date_select date, options = {}
|
149
|
+
options[:select_name] ||= ''
|
150
|
+
options[:select_id] ||= ''
|
151
|
+
options[:select_class] ||= ''
|
152
|
+
|
153
|
+
unless options[:select_id] == ''
|
154
|
+
day_id = " id='#{options[:select_id]}_day'"
|
155
|
+
month_id = " id='#{options[:select_id]}_month'"
|
156
|
+
year_id = " id='#{options[:select_id]}_year'"
|
157
|
+
else
|
158
|
+
day_id = ''
|
159
|
+
month_id = ''
|
160
|
+
year_id = ''
|
161
|
+
end
|
162
|
+
|
163
|
+
unless options[:select_name] == ''
|
164
|
+
day_name = " name='#{options[:select_name]}_day'"
|
165
|
+
month_name = " name='#{options[:select_name]}_month'"
|
166
|
+
year_name = " name='#{options[:select_name]}_year'"
|
167
|
+
else
|
168
|
+
day_name = ''
|
169
|
+
month_name = ''
|
170
|
+
year_name = ''
|
171
|
+
end
|
172
|
+
|
173
|
+
options[:day_first] ||= false
|
174
|
+
options[:start_year] ||= Time.now.strftime('%Y').to_i - 3
|
175
|
+
options[:end_year] ||= Time.now.strftime('%Y').to_i + 3
|
176
|
+
options[:month_name] ||= false
|
177
|
+
|
178
|
+
options[:start_year] = date.strftime('%Y').to_i if date.strftime('%Y').to_i < options[:start_year]
|
179
|
+
options[:end_year] = date.strftime('%Y').to_i if date.strftime('%Y').to_i > options[:end_year]
|
180
|
+
|
181
|
+
months = [
|
182
|
+
{ num: '01', name: 'January' },
|
183
|
+
{ num: '02', name: 'February' },
|
184
|
+
{ num: '03', name: 'March' },
|
185
|
+
{ num: '04', name: 'April' },
|
186
|
+
{ num: '05', name: 'May' },
|
187
|
+
{ num: '06', name: 'June' },
|
188
|
+
{ num: '07', name: 'July' },
|
189
|
+
{ num: '08', name: 'August' },
|
190
|
+
{ num: '09', name: 'September' },
|
191
|
+
{ num: '10', name: 'October' },
|
192
|
+
{ num: '11', name: 'November' },
|
193
|
+
{ num: '12', name: 'December' }
|
194
|
+
]
|
195
|
+
|
196
|
+
day = "<select class='day_select #{options[:select_class]}'#{day_id}#{day_name}'>"
|
197
|
+
(1..31).each do |d|
|
198
|
+
day << "<option value='#{d}' #{'selected' if d == date.strftime('%d').to_i}>#{d}</option>"
|
199
|
+
end
|
200
|
+
day << "</select>"
|
201
|
+
|
202
|
+
month = "<select class='month_select #{options[:select_class]}'#{month_id}#{month_name}>"
|
203
|
+
months.each do |m|
|
204
|
+
options[:month_name] ? d = m[:name] : d = m[:num]
|
205
|
+
month << "<option value='#{m[:num]}' #{'selected' if m[:num] == date.strftime('%m').to_i}>#{d}</option>"
|
206
|
+
end
|
207
|
+
month << "</select>"
|
208
|
+
|
209
|
+
year = "<select class='year_select #{options[:select_class]}'#{year_id}#{year_name}>"
|
210
|
+
(options[:start_year]..options[:end_year]).each do |y|
|
211
|
+
year << "<option value='#{y}' #{'selected' if y == date.strftime('%Y').to_i}>#{y}</option>"
|
212
|
+
end
|
213
|
+
year << "</select>"
|
214
|
+
|
215
|
+
if options[:day_first]
|
216
|
+
return "#{day} #{month} #{year}"
|
217
|
+
else
|
218
|
+
return "#{month} #{day} #{year}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
module ChassisExtras
|
225
|
+
|
226
|
+
# Adds Rack::Flash to the app (requires sessions)
|
227
|
+
def self.registered(app)
|
228
|
+
app.enable :sessions
|
229
|
+
app.set :session_secret, [*('A'..'Z')].sample(40).join if app.session_secret.nil?
|
230
|
+
use Rack::Flash
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
helpers ChassisExtraHelpers
|
236
|
+
register ChassisExtras
|
237
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Require this script in app Rakefiles to import the Chassis file_manager,
|
2
|
+
# the Chassis app rake tasks, and the tasks in the app /tasks directory.
|
3
|
+
|
4
|
+
require "#{File.dirname(__FILE__)}/file_manager"
|
5
|
+
|
6
|
+
Dir[File.join(File.dirname(__FILE__) + "/../tasks/**/*.rake")].each { |file| import file }
|
7
|
+
Dir["./tasks/**/*.rake"].each { |file| import file }
|
@@ -0,0 +1,40 @@
|
|
1
|
+
namespace :assets do
|
2
|
+
|
3
|
+
desc 'Compile .coffee and .sass files in /public'
|
4
|
+
task :precompile do
|
5
|
+
settings.assets_path.each do |path|
|
6
|
+
copy_directory("./#{path}", "./public/#{path}") unless path == 'public'
|
7
|
+
end
|
8
|
+
Dir.glob("./public/**/*.coffee").each do |asset|
|
9
|
+
File.open(asset.gsub('.coffee', '.js'), 'w') do |f|
|
10
|
+
f.write(CoffeeScript.compile(File.read(asset)))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
Dir.glob("./public/**/*.scss").each do |asset|
|
14
|
+
File.open(asset.gsub('.scss', '.css'), 'w') do |f|
|
15
|
+
f.write(Sass::Engine.for_file(asset, {}).render)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
settings.assets_path.each do |path|
|
19
|
+
unless path == 'public'
|
20
|
+
Dir.glob("./public/#{path}/**/*.{coffee,scss}").each do |asset|
|
21
|
+
File.delete(asset)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Remove compiled assets'
|
28
|
+
task :decompile do
|
29
|
+
settings.assets_path.each do |path|
|
30
|
+
FileUtils.rm_rf("./public/#{path}") unless path == 'public'
|
31
|
+
end
|
32
|
+
Dir.glob("./public/**/*.coffee").each do |asset|
|
33
|
+
File.delete(asset.gsub('.coffee', '.js')) if File.exists?(asset.gsub('.coffee', '.js'))
|
34
|
+
end
|
35
|
+
Dir.glob("./public/**/*.scss").each do |asset|
|
36
|
+
File.delete(asset.gsub('.scss', '.css')) if File.exists?(asset.gsub('.scss', '.css'))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
namespace :chassis do
|
2
|
+
|
3
|
+
desc 'Run app in an IRB session'
|
4
|
+
task :irb do
|
5
|
+
require 'irb'
|
6
|
+
ARGV.clear
|
7
|
+
IRB.start
|
8
|
+
end
|
9
|
+
|
10
|
+
namespace :run do
|
11
|
+
|
12
|
+
desc 'Run one or all /tests scripts'
|
13
|
+
task :test, :file do |t, args|
|
14
|
+
if args.file == nil
|
15
|
+
Dir["./tests/**/*.rb"].each { |file| require file }
|
16
|
+
else
|
17
|
+
require "./tests/#{args.file}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
namespace :setup do
|
2
|
+
|
3
|
+
desc 'Set up DataMapper'
|
4
|
+
task :datamapper do
|
5
|
+
create_directory './settings'
|
6
|
+
append_to_file './Gemfile', "\ngem 'data_mapper', '~> 1.2.0'"
|
7
|
+
append_to_file './Gemfile', "\ngem 'dm-sqlite-adapter', '~> 1.2.0'"
|
8
|
+
append_to_file './Gemfile', "\n# gem 'dm-mysql-adapter', '~> 1.2.0'"
|
9
|
+
append_to_file './Gemfile', "\n# gem 'dm-postgres-adapter', '~> 1.2.0'\n"
|
10
|
+
append_to_file './app.rb', "\nDataMapper.finalize\n"
|
11
|
+
copy_file "#{TEMPLATES}/datamapper/settings/datamapper.rb",
|
12
|
+
"./settings/datamapper.rb"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
namespace :dm do
|
18
|
+
|
19
|
+
def create_dm_path
|
20
|
+
if DataMapper.repository.adapter.options[:path].include? 'sqlite'
|
21
|
+
db = DataMapper.repository.adapter.options[:path].split('/').last
|
22
|
+
create_directory DataMapper.repository.adapter.options[:path].gsub(db, '')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
namespace :add do
|
27
|
+
|
28
|
+
desc 'Add a data migration' if defined? DataMapper
|
29
|
+
task :migration, :name do |t, args|
|
30
|
+
if args.name == nil
|
31
|
+
puts 'You must define a migration name.'
|
32
|
+
puts 'Example: rake add:migration[my_migration]'
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
create_directory './data', './data/migrations'
|
36
|
+
t = Time.now.strftime("%Y%m%d%H%M%S")
|
37
|
+
copy_template "#{TEMPLATES}/datamapper/data/migrations/datamapper_migration.rb",
|
38
|
+
"./data/migrations/#{t}_#{args.name}.rb",
|
39
|
+
{ t: t, migration: args.name }
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'Add a model' if defined? DataMapper
|
43
|
+
task :model, :name do |t, args|
|
44
|
+
if args.name == nil
|
45
|
+
puts 'You must define a model name.'
|
46
|
+
puts 'Example: rake add:model[my_model]'
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
create_directory './models', './tests', './tests/models'
|
50
|
+
copy_template "#{TEMPLATES}/datamapper/models/datamapper.rb",
|
51
|
+
"./models/#{args.name}.rb",
|
52
|
+
{ model: args.name }
|
53
|
+
copy_template "#{TEMPLATES}/datamapper/tests/models/datamapper_tests.rb",
|
54
|
+
"./tests/models/#{args.name}_tests.rb",
|
55
|
+
{ model: args.name }
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'Add a seed data script' if defined? DataMapper
|
59
|
+
task :seed, :name do |t, args|
|
60
|
+
if args.name == nil
|
61
|
+
puts 'You must define a seed file name.'
|
62
|
+
puts 'Example: rake add:seed[my_seed_script]'
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
create_directory './data', './data/seeds'
|
66
|
+
copy_template "#{TEMPLATES}/datamapper/data/seeds/seed.rb",
|
67
|
+
"./data/seeds/#{args.name}.rb"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
desc 'Auto upgrade one or all models' if defined? DataMapper
|
73
|
+
task :upgrade, :model do |t, args|
|
74
|
+
create_dm_path
|
75
|
+
args.model == nil ? DataMapper.auto_upgrade! : args.model.constantize.auto_upgrade!
|
76
|
+
end
|
77
|
+
|
78
|
+
desc 'Auto migrate one or all models' if defined? DataMapper
|
79
|
+
task :migrate, :model do |t, args|
|
80
|
+
create_dm_path
|
81
|
+
args.model == nil ? DataMapper.auto_migrate! : args.model.constantize.auto_migrate!
|
82
|
+
end
|
83
|
+
|
84
|
+
namespace :migrate do
|
85
|
+
|
86
|
+
desc 'Migrate up to a specific migration number' if defined? DataMapper
|
87
|
+
task :up, :number do |t, args|
|
88
|
+
create_dm_path
|
89
|
+
require 'dm-migrations/migration_runner'
|
90
|
+
Dir['./data/migrations/*.rb'].each { |m| require m }
|
91
|
+
args.number == nil ? migrate_up! : migrate_up!(args.number)
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'Migrate down to a specific migration number' if defined? DataMapper
|
95
|
+
task :down, :number do |t, args|
|
96
|
+
create_dm_path
|
97
|
+
require 'dm-migrations/migration_runner'
|
98
|
+
Dir['./data/migrations/*.rb'].each { |m| require m }
|
99
|
+
args.number == nil ? migrate_down! : migrate_down!(args.number)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
namespace :run do
|
105
|
+
|
106
|
+
desc 'Run one or all /data/seeds scripts' if defined? DataMapper
|
107
|
+
task :seed, :file do |t, args|
|
108
|
+
if args.file == nil
|
109
|
+
Dir["./data/seeds/**/*.rb"].each { |file| require file }
|
110
|
+
else
|
111
|
+
require "./data/seeds/#{args.file}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
namespace :sinatra do
|
2
|
+
|
3
|
+
namespace :add do
|
4
|
+
|
5
|
+
desc 'Add a namespaced /routes file'
|
6
|
+
task :routes, :namespace do |t, args|
|
7
|
+
if args.namespace == nil
|
8
|
+
puts 'You must define a route namespace.'
|
9
|
+
puts 'Example: rake sinatra:add:routes[my_routes]'
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
create_directory './routes', './tests', './tests/routes'
|
13
|
+
copy_template "#{TEMPLATES}/sinatra/routes/routes.rb",
|
14
|
+
"./routes/#{args.namespace}.rb",
|
15
|
+
{ namespace: args.namespace }
|
16
|
+
copy_template "#{TEMPLATES}/sinatra/tests/routes/routes_tests.rb",
|
17
|
+
"./tests/routes/#{args.namespace}_tests.rb",
|
18
|
+
{ namespace: args.namespace }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|