hobo 0.6 → 0.6.1
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/bin/hobo +2 -3
- data/hobo_files/plugin/CHANGES.txt +139 -0
- data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -8
- data/hobo_files/plugin/generators/hobo_front_controller/templates/controller.rb +1 -39
- data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +2 -2
- data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +27 -7
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +1 -2
- data/hobo_files/plugin/generators/hobo_user_controller/USAGE +34 -0
- data/hobo_files/plugin/generators/hobo_user_controller/hobo_user_controller_generator.rb +43 -0
- data/hobo_files/plugin/generators/hobo_user_controller/templates/controller.rb +5 -0
- data/hobo_files/plugin/generators/hobo_user_controller/templates/functional_test.rb +18 -0
- data/hobo_files/plugin/generators/hobo_user_controller/templates/helper.rb +2 -0
- data/hobo_files/plugin/generators/hobo_user_controller/templates/view.rhtml +2 -0
- data/hobo_files/plugin/init.rb +6 -2
- data/hobo_files/plugin/lib/extensions.rb +28 -41
- data/hobo_files/plugin/lib/hobo.rb +37 -17
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +25 -10
- data/hobo_files/plugin/lib/hobo/composite_model.rb +2 -2
- data/hobo_files/plugin/lib/hobo/controller.rb +8 -34
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +1 -1
- data/hobo_files/plugin/lib/hobo/dryml/part_context.rb +103 -0
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +10 -11
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +29 -72
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +11 -10
- data/hobo_files/plugin/lib/hobo/migrations.rb +12 -0
- data/hobo_files/plugin/lib/hobo/model.rb +3 -3
- data/hobo_files/plugin/lib/hobo/model_controller.rb +3 -3
- data/hobo_files/plugin/lib/hobo/rapid_helper.rb +3 -3
- data/hobo_files/plugin/lib/hobo/user_controller.rb +80 -0
- data/hobo_files/plugin/tags/rapid.dryml +36 -20
- data/hobo_files/plugin/tags/rapid_editing.dryml +4 -5
- data/hobo_files/plugin/tags/rapid_forms.dryml +6 -6
- data/hobo_files/plugin/tags/rapid_navigation.dryml +7 -5
- data/hobo_files/plugin/tags/rapid_pages.dryml +116 -10
- data/hobo_files/plugin/tags/rapid_support.dryml +23 -0
- metadata +13 -5
- data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +0 -42
- data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +0 -43
- data/hobo_files/plugin/lib/hobo/mapping_tags.rb +0 -262
data/bin/hobo
CHANGED
@@ -86,10 +86,9 @@ Dir.chdir(app_path) do
|
|
86
86
|
if user_model
|
87
87
|
puts "\nCreating #{user_model} model and controller...\n"
|
88
88
|
command("#{gen} hobo_user_model #{user_model}")
|
89
|
-
command("#{gen}
|
89
|
+
command("#{gen} hobo_user_controller #{user_model}")
|
90
90
|
end
|
91
91
|
|
92
92
|
puts "\nCreating standard pages...\n"
|
93
|
-
command("#{gen} hobo_front_controller front "
|
94
|
-
"#{'--no-user' unless user_model} --delete-index --add-routes")
|
93
|
+
command("#{gen} hobo_front_controller front --delete-index --add-routes")
|
95
94
|
end
|
@@ -1,3 +1,142 @@
|
|
1
|
+
=== Release 0.6.1 ===
|
2
|
+
|
3
|
+
Multiple user models
|
4
|
+
|
5
|
+
Hobo now supports multiple user models, with independent sign-up /
|
6
|
+
log-in/out for each.
|
7
|
+
|
8
|
+
The controller needs to declare hobo_user_controller instead of
|
9
|
+
hobo_model_controller (it still has all the hobo_model_controller
|
10
|
+
features). e.g.
|
11
|
+
|
12
|
+
class AdminsController; hobo_user_controller; end
|
13
|
+
|
14
|
+
Hobo routing will automatically give you
|
15
|
+
|
16
|
+
/admin_login
|
17
|
+
/admin_logout
|
18
|
+
/admin_signup
|
19
|
+
|
20
|
+
Note this controller will now filter loggin of passwords.
|
21
|
+
|
22
|
+
The model needs to include Hobo::AuthenticatedUser and declare a
|
23
|
+
login attribute with, e.g.
|
24
|
+
|
25
|
+
set_login_attr :email
|
26
|
+
|
27
|
+
The hobo_front_controller generator no longer generates anything
|
28
|
+
related to users - it's now just the fron page and search page.
|
29
|
+
|
30
|
+
There is a new hobo_user_model generator to create these
|
31
|
+
controllers.
|
32
|
+
|
33
|
+
The global value Hobo.user_model is gone
|
34
|
+
|
35
|
+
Rapid now has <LoginPage> and <SignupPage> templates. They're not so
|
36
|
+
customisable just now but you can of course replace them completely
|
37
|
+
with your own views.
|
38
|
+
|
39
|
+
|
40
|
+
Misc fixes
|
41
|
+
|
42
|
+
Search was broken
|
43
|
+
|
44
|
+
The 'hobo' command tried to connect to the DB, which was a problem
|
45
|
+
as you'd had no chance to configure database.yml
|
46
|
+
|
47
|
+
|
48
|
+
Rapid
|
49
|
+
|
50
|
+
Fix: reset_form and refocus form options
|
51
|
+
|
52
|
+
<Table> now supports @fields, allowing you to give a bunch of field
|
53
|
+
names and automatically have a table with a column for each of those
|
54
|
+
fields. There's also a <controls> param that can be used to have a
|
55
|
+
delete button and edit link on each row. As usual there's a ton of
|
56
|
+
customisation options - see the source.
|
57
|
+
|
58
|
+
<human_type> is now <type_name> and takes flags @plural and @lowercase
|
59
|
+
|
60
|
+
<editor> and <input> have better automatic css classes
|
61
|
+
|
62
|
+
Restored old behaviour where <page_nav/> would display nothing when
|
63
|
+
there's only one page.
|
64
|
+
|
65
|
+
Finally created <EditPage>
|
66
|
+
|
67
|
+
Small cleanups in the rapid pages
|
68
|
+
|
69
|
+
New tag <with_field_names/> used in <Table> to iterate over the
|
70
|
+
column headings.
|
71
|
+
|
72
|
+
<view> for Time objects now supports @format - a strftime style
|
73
|
+
format string.
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
Migration generator
|
78
|
+
|
79
|
+
Fix: Edge Rails now dumps out in sexy format, which was freaking the
|
80
|
+
migration generator.
|
81
|
+
|
82
|
+
You can now configure the generator to completely ignore specified
|
83
|
+
tables and models. In environment.rb do
|
84
|
+
|
85
|
+
Hobo::Migrations.ignore = %w(red_fish blue_fish)
|
86
|
+
|
87
|
+
That will ignore the models RedFish and BlueFish, and the tables
|
88
|
+
red_fishes and blue_fishes
|
89
|
+
|
90
|
+
To ignore just tables, assign an array of their names to
|
91
|
+
|
92
|
+
Hobo::Migrations.ignore_tables
|
93
|
+
|
94
|
+
To ignore just models, assign an array of the class names to
|
95
|
+
|
96
|
+
Hobo::Migrations.ignore_models
|
97
|
+
|
98
|
+
New secure ajax parts mechanism
|
99
|
+
|
100
|
+
The new mechanism stores the part state in a base 64 encoded string,
|
101
|
+
complete with an SHA1 (by default) digest, so malicious tampering
|
102
|
+
with the part state is prevented.
|
103
|
+
|
104
|
+
Parts can now capture the state of local variables along with the
|
105
|
+
DRYML context. Give a list of locals you wish to capture in the
|
106
|
+
'part_locals' attribute. e.g.
|
107
|
+
|
108
|
+
<div part="my_part" part_locals="a, b"> ... </div>
|
109
|
+
|
110
|
+
(the inability to do this was a serious limitation of the previous
|
111
|
+
mechanism)
|
112
|
+
|
113
|
+
|
114
|
+
Core extensions
|
115
|
+
|
116
|
+
Added Methodphitamine and removed omap, oselect, ofind, oany oall.
|
117
|
+
|
118
|
+
See http://jicksta.com/articles/2007/08/04/the-methodphitamine
|
119
|
+
|
120
|
+
|
121
|
+
DRYML
|
122
|
+
|
123
|
+
merge_attrs fixed to not complain about nils
|
124
|
+
|
125
|
+
Fix: css classes were not accumulated in some template parameter
|
126
|
+
situations
|
127
|
+
|
128
|
+
|
129
|
+
Hobo module
|
130
|
+
|
131
|
+
#type_name renamed to #type_id
|
132
|
+
|
133
|
+
#object_from_dom_id will now return classes when the id has no, er, id. As in
|
134
|
+
|
135
|
+
Hobo.object_from_dom_id("blog_post") #=> BlogPost
|
136
|
+
|
137
|
+
Similarly, #dom_id generates those kind of ID for classes
|
138
|
+
|
139
|
+
|
1
140
|
=== Release 0.6 ===
|
2
141
|
|
3
142
|
DRYML
|
@@ -34,7 +34,7 @@ class HoboFrontControllerGenerator < Rails::Generator::NamedBase
|
|
34
34
|
File.join('app/helpers', class_path, "#{file_name}_helper.rb"))
|
35
35
|
|
36
36
|
|
37
|
-
pages = options[:no_user] ? %w{index search} : %w{index search
|
37
|
+
pages = options[:no_user] ? %w{index search} : %w{index search}
|
38
38
|
for page in pages
|
39
39
|
m.template("#{page}.dryml", File.join('app/views', class_path, file_name, "#{page}.dryml"))
|
40
40
|
end
|
@@ -51,11 +51,6 @@ class HoboFrontControllerGenerator < Rails::Generator::NamedBase
|
|
51
51
|
|
52
52
|
route = (" map.search 'search', :controller => '#{name}', :action => 'search'\n" +
|
53
53
|
" map.homepage '', :controller => '#{name}', :action => 'index'")
|
54
|
-
unless options[:no_user]
|
55
|
-
route += ("\n map.login 'login', :controller => '#{name}', :action => 'login'\n" +
|
56
|
-
" map.logout 'logout', :controller => '#{name}', :action => 'logout'\n" +
|
57
|
-
" map.signup 'signup', :controller => '#{name}', :action => 'signup'")
|
58
|
-
end
|
59
54
|
|
60
55
|
route_src = File.read(routes_path)
|
61
56
|
return if route_src.include?(route)
|
@@ -81,8 +76,6 @@ class HoboFrontControllerGenerator < Rails::Generator::NamedBase
|
|
81
76
|
opt.separator 'Options:'
|
82
77
|
opt.on("--add-routes",
|
83
78
|
"Modify config/routes.rb to support the front controller") { |v| options[:add_routes] = true }
|
84
|
-
opt.on("--no-user",
|
85
|
-
"Don't create the login and signup pages") { |v| options[:no_user] = true }
|
86
79
|
opt.on("--delete-index",
|
87
80
|
"Delete public/index.html") { |v| options[:delete_index] = true }
|
88
81
|
end
|
@@ -2,50 +2,12 @@ class <%= class_name %>Controller < ApplicationController
|
|
2
2
|
|
3
3
|
hobo_controller
|
4
4
|
|
5
|
-
filter_parameter_logging "password"
|
6
|
-
|
7
5
|
def index; end
|
8
6
|
|
9
7
|
def search
|
10
|
-
if
|
8
|
+
if params[:query]
|
11
9
|
site_search(params[:query])
|
12
10
|
end
|
13
11
|
end
|
14
12
|
|
15
|
-
def login
|
16
|
-
return unless request.post?
|
17
|
-
user = Hobo.user_model.authenticate(params[:login], params[:password])
|
18
|
-
if user
|
19
|
-
self.current_user = user
|
20
|
-
if params[:remember_me] == "1"
|
21
|
-
self.current_user.remember_me
|
22
|
-
cookies[:auth_token] = { :value => self.current_user.remember_token ,
|
23
|
-
:expires => self.current_user.remember_token_expires_at }
|
24
|
-
end
|
25
|
-
redirect_back_or_default(:action => 'index')
|
26
|
-
flash[:notice] = "You have logged in"
|
27
|
-
else
|
28
|
-
flash[:notice] = "You did not provide a valid login and password"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def signup
|
33
|
-
@user = Hobo.user_model.new(params[:user])
|
34
|
-
return unless request.post?
|
35
|
-
@user.save!
|
36
|
-
self.current_user = @user
|
37
|
-
redirect_back_or_default(:action => 'index')
|
38
|
-
flash[:notice] = "Thanks for signing up!"
|
39
|
-
rescue ActiveRecord::RecordInvalid
|
40
|
-
render :action => 'signup'
|
41
|
-
end
|
42
|
-
|
43
|
-
def logout
|
44
|
-
self.current_user.forget_me if logged_in?
|
45
|
-
cookies.delete :auth_token
|
46
|
-
reset_session
|
47
|
-
flash[:notice] = "You have been logged out."
|
48
|
-
redirect_back_or_default(:action => 'index')
|
49
|
-
end
|
50
|
-
|
51
13
|
end
|
@@ -19,8 +19,8 @@
|
|
19
19
|
<section>
|
20
20
|
<if test="&this.count == 0">
|
21
21
|
<p>There are no <%%= this.name.titleize.pluralize %></p>
|
22
|
-
<if test="&can_create?(this)
|
23
|
-
<p>Create a <a to="&this
|
22
|
+
<if test="&can_create?(this)">
|
23
|
+
<p>Create a <a to="&this" action="new"/>.</p>
|
24
24
|
</if>
|
25
25
|
</if>
|
26
26
|
<else>
|
@@ -15,13 +15,19 @@ class HoboMigrationGenerator < Rails::Generator::Base
|
|
15
15
|
# Force load of hobo models
|
16
16
|
Hobo.models
|
17
17
|
|
18
|
+
ignore_tables = Hobo::Migrations.ignore_tables + Hobo::Migrations.ignore.every(:pluralize)
|
19
|
+
ignore_models = (Hobo::Migrations.ignore + Hobo::Migrations.ignore_models).every(:underscore)
|
20
|
+
|
21
|
+
db_tables = connection.tables - ignore_tables
|
22
|
+
|
18
23
|
models = ActiveRecord::Base.send(:subclasses).reject {|c| c.name.starts_with?("CGI::") }
|
24
|
+
models = models.reject {|m| m.name.underscore.in?(ignore_models) }
|
19
25
|
table_models = models.index_by {|m| m.table_name}
|
20
26
|
model_table_names = models.every(:table_name)
|
21
27
|
|
22
|
-
to_create = model_table_names -
|
23
|
-
to_drop =
|
24
|
-
to_change =
|
28
|
+
to_create = model_table_names - db_tables
|
29
|
+
to_drop = db_tables - model_table_names - ['schema_info']
|
30
|
+
to_change = db_tables & model_table_names
|
25
31
|
|
26
32
|
to_rename = rename_or_drop!(to_create, to_drop, "table")
|
27
33
|
|
@@ -54,8 +60,8 @@ class HoboMigrationGenerator < Rails::Generator::Base
|
|
54
60
|
undo_changes << undo
|
55
61
|
end
|
56
62
|
|
57
|
-
up = [renames, drops, creates, changes
|
58
|
-
down = [undo_renames, undo_drops, undo_creates, undo_changes
|
63
|
+
up = [renames, drops, creates, changes].flatten.select{|s|!s.blank?} * "\n\n"
|
64
|
+
down = [undo_renames, undo_drops, undo_creates, undo_changes].flatten.select{|s|!s.blank?} * "\n\n"
|
59
65
|
|
60
66
|
puts "\n---------- Up Migration ----------", up, "----------------------------------"
|
61
67
|
puts "\n---------- Down Migration --------", down, "----------------------------------"
|
@@ -210,14 +216,28 @@ class HoboMigrationGenerator < Rails::Generator::Base
|
|
210
216
|
res.string.strip.gsub("\n ", "\n")
|
211
217
|
end
|
212
218
|
|
219
|
+
def column_options_from_reverted_table(table, column)
|
220
|
+
revert = revert_table(table)
|
221
|
+
if (md = revert.match(/\s*t\.column\s+"#{column}",\s+(:[a-zA-Z0-9_]+)(?:,\s+(.*?)$)?/m))
|
222
|
+
# Ugly migration
|
223
|
+
_, type, options = *md
|
224
|
+
elsif (md = revert.match(/\s*t\.([a-z_]+)\s+"#{column}"(?:,\s+(.*?)$)?/m))
|
225
|
+
# Sexy migration
|
226
|
+
_, type, options = *md
|
227
|
+
type = ":#{type}"
|
228
|
+
end
|
229
|
+
[type, options]
|
230
|
+
end
|
231
|
+
|
213
232
|
|
214
233
|
def change_column_back(table, column)
|
215
|
-
|
234
|
+
type, options = column_options_from_reverted_table(table, column)
|
216
235
|
"change_column :#{table}, :#{column}, #{type}#{', ' + options.strip if options}"
|
217
236
|
end
|
218
237
|
|
219
238
|
def revert_column(table, column)
|
220
|
-
|
239
|
+
type, options = column_options_from_reverted_table(table, column)
|
240
|
+
"add_column :#{table}, :#{column}, #{type}#{', ' + options.strip if options}"
|
221
241
|
end
|
222
242
|
|
223
243
|
|
@@ -48,9 +48,8 @@ var Hobo = {
|
|
48
48
|
if (updates.length > 0) {
|
49
49
|
updates.each(function(dom_id) {
|
50
50
|
if (!hoboParts[dom_id]) { throw "Update of dom-id that is not a part: " + dom_id }
|
51
|
-
params.push("render["+i+"][
|
51
|
+
params.push("render["+i+"][part_context]=" + hoboParts[dom_id])
|
52
52
|
params.push("render["+i+"][id]=" + dom_id)
|
53
|
-
params.push("render["+i+"][object]=" + hoboParts[dom_id][1])
|
54
53
|
i += 1
|
55
54
|
})
|
56
55
|
params.push("part_page=" + hoboPartPage)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Description:
|
2
|
+
|
3
|
+
The controller generator creates a controller geared up to handle
|
4
|
+
login and signup for a specific user model. It can also be used as
|
5
|
+
a regular hobo_model_controller.
|
6
|
+
|
7
|
+
The generator takes a controller name and a list of views as
|
8
|
+
arguments. The controller name may be given in CamelCase or
|
9
|
+
under_score and should not be suffixed with 'Controller'. To
|
10
|
+
create a controller within a module, specify the controller name
|
11
|
+
as 'module/controller'.
|
12
|
+
|
13
|
+
The generator creates a controller class in app/controllers with view
|
14
|
+
templates in app/views/controller_name, a helper class in app/helpers,
|
15
|
+
and a functional test suite in test/functional.
|
16
|
+
|
17
|
+
Example:
|
18
|
+
./script/generate controller CreditCard open debit credit close
|
19
|
+
|
20
|
+
Credit card controller with URLs like /credit_card/debit.
|
21
|
+
Controller: app/controllers/credit_card_controller.rb
|
22
|
+
Views: app/views/credit_card/debit.rhtml [...]
|
23
|
+
Helper: app/helpers/credit_card_helper.rb
|
24
|
+
Test: test/functional/credit_card_controller_test.rb
|
25
|
+
|
26
|
+
Modules Example:
|
27
|
+
./script/generate hobo_user_controller 'admin/credit_card' suspend late_fee
|
28
|
+
|
29
|
+
Credit card admin controller with URLs /admin/credit_card/suspend.
|
30
|
+
Controller: app/controllers/admin/credit_card_controller.rb
|
31
|
+
Views: app/views/admin/credit_card/debit.rhtml [...]
|
32
|
+
Helper: app/helpers/admin/credit_card_helper.rb
|
33
|
+
Test: test/functional/admin/credit_card_controller_test.rb
|
34
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class HoboUserControllerGenerator < Rails::Generator::NamedBase
|
2
|
+
|
3
|
+
def initialize(args, options)
|
4
|
+
args[0] = args[0].pluralize
|
5
|
+
super(args, options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def manifest
|
9
|
+
record do |m|
|
10
|
+
# Check for class naming collisions.
|
11
|
+
m.class_collisions class_path, "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper"
|
12
|
+
|
13
|
+
# Controller, helper, views, and test directories.
|
14
|
+
m.directory File.join('app/controllers', class_path)
|
15
|
+
m.directory File.join('app/helpers', class_path)
|
16
|
+
m.directory File.join('app/views', class_path, file_name)
|
17
|
+
m.directory File.join('test/functional', class_path)
|
18
|
+
|
19
|
+
# Controller class, functional test, and helper class.
|
20
|
+
m.template 'controller.rb',
|
21
|
+
File.join('app/controllers',
|
22
|
+
class_path,
|
23
|
+
"#{file_name}_controller.rb")
|
24
|
+
|
25
|
+
m.template 'functional_test.rb',
|
26
|
+
File.join('test/functional',
|
27
|
+
class_path,
|
28
|
+
"#{file_name}_controller_test.rb")
|
29
|
+
|
30
|
+
m.template 'helper.rb',
|
31
|
+
File.join('app/helpers',
|
32
|
+
class_path,
|
33
|
+
"#{file_name}_helper.rb")
|
34
|
+
|
35
|
+
# View template for each action.
|
36
|
+
actions.each do |action|
|
37
|
+
path = File.join('app/views', class_path, file_name, "#{action}.rhtml")
|
38
|
+
m.template 'view.rhtml', path,
|
39
|
+
:assigns => { :action => action, :path => path }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
|
2
|
+
require '<%= file_path %>_controller'
|
3
|
+
|
4
|
+
# Re-raise errors caught by the controller.
|
5
|
+
class <%= class_name %>Controller; def rescue_action(e) raise e end; end
|
6
|
+
|
7
|
+
class <%= class_name %>ControllerTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@controller = <%= class_name %>Controller.new
|
10
|
+
@request = ActionController::TestRequest.new
|
11
|
+
@response = ActionController::TestResponse.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Replace this with your real tests.
|
15
|
+
def test_truth
|
16
|
+
assert true
|
17
|
+
end
|
18
|
+
end
|
data/hobo_files/plugin/init.rb
CHANGED
@@ -24,6 +24,12 @@ ActionView::Base.register_template_handler("dryml", Hobo::Dryml::TemplateHandler
|
|
24
24
|
|
25
25
|
class ActionController::Base
|
26
26
|
|
27
|
+
def self.hobo_user_controller(model=nil)
|
28
|
+
include Hobo::ModelController
|
29
|
+
self.model = model if model
|
30
|
+
include Hobo::UserController
|
31
|
+
end
|
32
|
+
|
27
33
|
def self.hobo_model_controller(model=nil)
|
28
34
|
include Hobo::ModelController
|
29
35
|
self.model = model if model
|
@@ -43,6 +49,4 @@ end
|
|
43
49
|
|
44
50
|
# Default settings
|
45
51
|
|
46
|
-
Hobo.user_model ||= (User rescue (Person rescue nil))
|
47
|
-
|
48
52
|
Hobo.developer_features = ["development", "test"].include?(RAILS_ENV) if Hobo.developer_features? == nil
|