sinatra-chassis 1.0.0
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 +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
|