summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel DeLeo <dan@opscode.com>2010-04-25 23:04:43 -0700
committerDaniel DeLeo <dan@opscode.com>2010-05-07 10:58:29 -0700
commit5a66520cdd2e24f37a07489c52e18081f7b024dc (patch)
treec238b120b60cf5120b40527e3a06a9c1d0a77e61
parentf60fdd9d917e2391f1bdadebe2e22ba8f65704cc (diff)
downloadchef-5a66520cdd2e24f37a07489c52e18081f7b024dc.tar.gz
[CHEF-1072] cleanup and fixes for merb 1.1
-rw-r--r--Rakefile4
-rw-r--r--chef-server-api/Rakefile3
-rw-r--r--chef-server-api/app/controllers/application.rb4
-rw-r--r--chef-server-api/app/controllers/cookbooks.rb3
-rw-r--r--chef-server-api/app/helpers/application_helper.rb163
-rw-r--r--chef-server-api/app/helpers/exceptions_helper.rb6
-rw-r--r--chef-server-api/app/helpers/global_helpers.rb25
-rw-r--r--chef-server-api/app/helpers/nodes_helper.rb26
-rw-r--r--chef-server-api/app/helpers/roles_helper.rb5
-rw-r--r--chef-server-api/app/helpers/tarball_helper.rb106
-rwxr-xr-xchef-server-api/bin/chef-server37
-rw-r--r--chef-server-api/config/environments/development.rb2
-rw-r--r--chef-server-api/config/rack.rb5
-rw-r--r--chef-server-api/config/router.rb2
-rw-r--r--chef-server-api/lib/chef-server-api.rb2
15 files changed, 84 insertions, 309 deletions
diff --git a/Rakefile b/Rakefile
index f2c1a665fa..e69af7dc91 100644
--- a/Rakefile
+++ b/Rakefile
@@ -113,10 +113,10 @@ def start_chef_server(type="normal")
case type
when "normal"
puts "Starting chef server for development with './chef-server/bin/chef-server -a thin -l debug -N'"
- exec("./chef-server/bin/chef-server -a thin -l debug -N")
+ exec("./chef-server-api/bin/chef-server -a thin -l debug -N")
when "features"
puts "Starting chef server for features with #{["./chef-server/bin/chef-server -a thin -C #{File.join(File.dirname(__FILE__), "features", "data", "config", "server.rb")} -l debug -N"].join(' ')}"
- exec("./chef-server/bin/chef-server -a thin -C #{File.join(File.dirname(__FILE__), "features", "data", "config", "server.rb")} -l debug -N")
+ exec("./chef-server-api/bin/chef-server -a thin -C #{File.join(File.dirname(__FILE__), "features", "data", "config", "server.rb")} -l debug -N")
end
end
end
diff --git a/chef-server-api/Rakefile b/chef-server-api/Rakefile
index 79f824d37b..1320829944 100644
--- a/chef-server-api/Rakefile
+++ b/chef-server-api/Rakefile
@@ -33,6 +33,9 @@ spec = Gem::Specification.new do |s|
s.add_dependency "uuidtools", "~> 2.1.1"
s.add_dependency "thin"
+
+ s.bindir = "bin"
+ s.executables = %w( chef-server )
s.require_path = 'lib'
s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{config,lib,spec,app,public,stubs}/**/*")
diff --git a/chef-server-api/app/controllers/application.rb b/chef-server-api/app/controllers/application.rb
index 9cd8a08ea6..8f5f3025e5 100644
--- a/chef-server-api/app/controllers/application.rb
+++ b/chef-server-api/app/controllers/application.rb
@@ -17,8 +17,8 @@
# limitations under the License.
#
-require "chef" / "mixin" / "checksum"
-require "chef" / "cookbook_loader"
+require "chef/mixin/checksum"
+require "chef/cookbook_loader"
require "mixlib/authentication/signatureverification"
class Application < Merb::Controller
diff --git a/chef-server-api/app/controllers/cookbooks.rb b/chef-server-api/app/controllers/cookbooks.rb
index ce9480bade..d9d077be4c 100644
--- a/chef-server-api/app/controllers/cookbooks.rb
+++ b/chef-server-api/app/controllers/cookbooks.rb
@@ -28,7 +28,7 @@ class Cookbooks < Application
before :authenticate_every
include Chef::Mixin::Checksum
- include Merb::ChefServerApi::TarballHelper
+ include Merb::TarballHelper
def index
cl = Chef::CookbookLoader.new
@@ -179,7 +179,6 @@ class Cookbooks < Application
cookbook_name = params[:cookbook_id]
expected_location = cookbook_location(cookbook_name)
raise NotFound, "Cannot find cookbook named #{cookbook_name} at #{expected_location}. Note: Tarball generation only applies to cookbooks under the first directory in the server's Chef::Config.cookbook_path variable and does to apply overrides." unless File.directory? expected_location
-
send_file(get_or_create_cookbook_tarball_location(cookbook_name))
end
diff --git a/chef-server-api/app/helpers/application_helper.rb b/chef-server-api/app/helpers/application_helper.rb
deleted file mode 100644
index 321c44e366..0000000000
--- a/chef-server-api/app/helpers/application_helper.rb
+++ /dev/null
@@ -1,163 +0,0 @@
-module Merb
- module ChefServerApi
- module ApplicationHelper
-
- # Generate the absolute url for a slice - takes the slice's :path_prefix into account.
- #
- # @param slice_name<Symbol>
- # The name of the slice - in identifier_sym format (underscored).
- # @param *args<Array[Symbol,Hash]>
- # There are several possibilities regarding arguments:
- # - when passing a Hash only, the :default route of the current
- # slice will be used
- # - when a Symbol is passed, it's used as the route name
- # - a Hash with additional params can optionally be passed
- #
- # @return <String> A uri based on the requested slice.
- #
- # @example absolute_url(:awesome, :format => 'html')
- # @example absolute_url(:forum, :posts, :format => 'xml')
- def absolute_url(slice_name, *args)
- options = extract_options_from_args!(args) || {}
- protocol = options.delete(:protocol) || request.protocol
- host = options.delete(:host) || request.host
-
- protocol + "://" + host + slice_url(slice_name,*args)
- end
-
- # @param *segments<Array[#to_s]> Path segments to append.
- #
- # @return <String>
- # A path relative to the public directory, with added segments.
- def image_path(*segments)
- public_path_for(:image, *segments)
- end
-
- # @param *segments<Array[#to_s]> Path segments to append.
- #
- # @return <String>
- # A path relative to the public directory, with added segments.
- def javascript_path(*segments)
- public_path_for(:javascript, *segments)
- end
-
- # @param *segments<Array[#to_s]> Path segments to append.
- #
- # @return <String>
- # A path relative to the public directory, with added segments.
- def stylesheet_path(*segments)
- public_path_for(:stylesheet, *segments)
- end
-
- # Construct a path relative to the public directory
- #
- # @param <Symbol> The type of component.
- # @param *segments<Array[#to_s]> Path segments to append.
- #
- # @return <String>
- # A path relative to the public directory, with added segments.
- def public_path_for(type, *segments)
- ::ChefServerApi.public_path_for(type, *segments)
- end
-
- # Construct an app-level path.
- #
- # @param <Symbol> The type of component.
- # @param *segments<Array[#to_s]> Path segments to append.
- #
- # @return <String>
- # A path within the host application, with added segments.
- def app_path_for(type, *segments)
- ::ChefServerApi.app_path_for(type, *segments)
- end
-
- # Construct a slice-level path.
- #
- # @param <Symbol> The type of component.
- # @param *segments<Array[#to_s]> Path segments to append.
- #
- # @return <String>
- # A path within the slice source (Gem), with added segments.
- def slice_path_for(type, *segments)
- ::ChefServerApi.slice_path_for(type, *segments)
- end
-
- def build_tree(name, node, default={}, override={})
- node = Chef::Mixin::DeepMerge.merge(default, node)
- node = Chef::Mixin::DeepMerge.merge(node, override)
- html = "<table id='#{name}' class='tree table'>"
- html << "<tr><th class='first'>Attribute</th><th class='last'>Value</th></tr>"
- count = 0
- parent = 0
- append_tree(name, html, node, count, parent, override)
- html << "</table>"
- html
- end
-
- def append_tree(name, html, node, count, parent, override)
- node.sort{ |a,b| a[0] <=> b[0] }.each do |key, value|
- to_send = Array.new
- count += 1
- is_parent = false
- local_html = ""
- local_html << "<tr id='#{name}-#{count}' class='collapsed #{name}"
- if parent != 0
- local_html << " child-of-#{name}-#{parent}' style='display: none;'>"
- else
- local_html << "'>"
- end
- local_html << "<td class='table-key'><span toggle='#{name}-#{count}'/>#{key}</td>"
- case value
- when Hash
- is_parent = true
- local_html << "<td></td>"
- p = count
- to_send << Proc.new { append_tree(name, html, value, count, p, override) }
- when Array
- is_parent = true
- local_html << "<td></td>"
- as_hash = {}
- value.each_index { |i| as_hash[i] = value[i] }
- p = count
- to_send << Proc.new { append_tree(name, html, as_hash, count, p, override) }
- else
- local_html << "<td><div class='json-attr'>#{value}</div></td>"
- end
- local_html << "</tr>"
- local_html.sub!(/class='collapsed/, 'class=\'collapsed parent') if is_parent
- local_html.sub!(/<span/, "<span class='expander'") if is_parent
- html << local_html
- to_send.each { |s| count = s.call }
- count += to_send.length
- end
- count
- end
-
- # Recursively build a tree of lists.
- #def build_tree(node)
- # list = "<dl>"
- # list << "\n<!-- Beginning of Tree -->"
- # walk = lambda do |key,value|
- # case value
- # when Hash, Array
- # list << "\n<!-- Beginning of Enumerable obj -->"
- # list << "\n<dt>#{key}</dt>"
- # list << "<dd>"
- # list << "\t<dl>\n"
- # value.each(&walk)
- # list << "\t</dl>\n"
- # list << "</dd>"
- # list << "\n<!-- End of Enumerable obj -->"
- #
- # else
- # list << "\n<dt>#{key}</dt>"
- # list << "<dd>#{value}</dd>"
- # end
- # end
- # node.sort{ |a,b| a[0] <=> b[0] }.each(&walk)
- # list << "</dl>"
- #end
-
- end
- end
-end
diff --git a/chef-server-api/app/helpers/exceptions_helper.rb b/chef-server-api/app/helpers/exceptions_helper.rb
deleted file mode 100644
index 18a41965b6..0000000000
--- a/chef-server-api/app/helpers/exceptions_helper.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Merb
- module ChefServerApi
- module ExceptionsHelper
- end
- end
-end # Merb
diff --git a/chef-server-api/app/helpers/global_helpers.rb b/chef-server-api/app/helpers/global_helpers.rb
deleted file mode 100644
index 93a94e7f56..0000000000
--- a/chef-server-api/app/helpers/global_helpers.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-module Merb
- module ChefServerApi
-
- module GlobalHelpers
- end
- end
-end
diff --git a/chef-server-api/app/helpers/nodes_helper.rb b/chef-server-api/app/helpers/nodes_helper.rb
deleted file mode 100644
index 0204baba8e..0000000000
--- a/chef-server-api/app/helpers/nodes_helper.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Author:: Adam Jacob (<adam@opscode.com>)
-# Author:: AJ Christensen (<aj@junglist.gen.nz>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-module Merb
- module ChefServerApi
- module NodesHelper
-
- end
- end
-end
diff --git a/chef-server-api/app/helpers/roles_helper.rb b/chef-server-api/app/helpers/roles_helper.rb
deleted file mode 100644
index b6d46cdcd1..0000000000
--- a/chef-server-api/app/helpers/roles_helper.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Merb
- module RolesHelper
-
- end
-end # Merb \ No newline at end of file
diff --git a/chef-server-api/app/helpers/tarball_helper.rb b/chef-server-api/app/helpers/tarball_helper.rb
index cd2a7902ea..8cafc497fd 100644
--- a/chef-server-api/app/helpers/tarball_helper.rb
+++ b/chef-server-api/app/helpers/tarball_helper.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,66 +17,64 @@
#
module Merb
- module ChefServerApi
- module TarballHelper
+ module TarballHelper
- class FileParameterException < StandardError ; end
+ class FileParameterException < StandardError ; end
- def validate_file_parameter(cookbook_name, file_param)
- raise FileParameterException, "missing required parameter: file" unless file_param
- raise FileParameterException, "invalid parameter: file must be a File" unless file_param.respond_to?(:has_key?) && file_param[:tempfile].respond_to?(:read)
- tarball_path = file_param[:tempfile].path
- raise FileParameterException, "invalid tarball: (try creating with 'tar czf cookbook.tar.gz cookbook/')" unless system("tar", "tzf", tarball_path)
- entry_roots = `tar tzf #{tarball_path}`.split("\n").map{|e|(e.split('/')-['.']).first}.uniq
- raise FileParameterException, "invalid tarball: tarball root must contain #{cookbook_name}" unless entry_roots.include?(cookbook_name)
- end
+ def validate_file_parameter(cookbook_name, file_param)
+ raise FileParameterException, "missing required parameter: file" unless file_param
+ raise FileParameterException, "invalid parameter: file must be a File" unless file_param.respond_to?(:has_key?) && file_param[:tempfile].respond_to?(:read)
+ tarball_path = file_param[:tempfile].path
+ raise FileParameterException, "invalid tarball: (try creating with 'tar czf cookbook.tar.gz cookbook/')" unless system("tar", "tzf", tarball_path)
+ entry_roots = `tar tzf #{tarball_path}`.split("\n").map{|e|(e.split('/')-['.']).first}.uniq
+ raise FileParameterException, "invalid tarball: tarball root must contain #{cookbook_name}" unless entry_roots.include?(cookbook_name)
+ end
- def cookbook_base
- [Chef::Config.cookbook_path].flatten.first
- end
-
- def cookbook_location(cookbook_name)
- File.join(cookbook_base, cookbook_name)
- end
+ def cookbook_base
+ [Chef::Config.cookbook_path].flatten.first
+ end
- def cookbook_tarball_location(cookbook_name)
- File.join(Chef::Config.cookbook_tarball_path, "#{cookbook_name}.tar.gz")
- end
-
- def get_or_create_cookbook_tarball_location(cookbook_name)
- tarball_location = cookbook_tarball_location(cookbook_name)
- unless File.exists? tarball_location
- args = ["tar", "-C", cookbook_base, "-czf", tarball_location, cookbook_name]
- Chef::Log.debug("Tarball for #{cookbook_name} not found, so creating at #{tarball_location} with '#{args.join(' ')}'")
- FileUtils.mkdir_p(Chef::Config.cookbook_tarball_path)
- system(*args)
- end
- tarball_location
- end
+ def cookbook_location(cookbook_name)
+ File.join(cookbook_base, cookbook_name)
+ end
- def expand_tarball_and_put_in_repository(cookbook_name, file)
- # untar cookbook tarball into tempdir
- tempdir = File.join("#{file.path}.data")
- Chef::Log.debug("Creating #{tempdir} and untarring #{file.path} into it")
- FileUtils.mkdir_p(tempdir)
- raise "Could not untar file" unless system("tar", "xzf", file.path, "-C", tempdir)
-
- cookbook_path = cookbook_location(cookbook_name)
- tarball_path = cookbook_tarball_location(cookbook_name)
-
- # clear any existing cookbook components and move tempdir into the repository
- Chef::Log.debug("Moving #{tempdir} to #{cookbook_path}")
- FileUtils.rm_rf(cookbook_path)
- FileUtils.mkdir_p(cookbook_path)
- Dir[File.join(tempdir, cookbook_name, "*")].each{|e| FileUtils.mv(e, cookbook_path)}
+ def cookbook_tarball_location(cookbook_name)
+ File.join(Chef::Config.cookbook_tarball_path, "#{cookbook_name}.tar.gz")
+ end
- # clear the existing tarball (if exists) and move the downloaded tarball to the cache
- Chef::Log.debug("Moving #{file.path} to #{tarball_path}")
+ def get_or_create_cookbook_tarball_location(cookbook_name)
+ tarball_location = cookbook_tarball_location(cookbook_name)
+ unless File.exists? tarball_location
+ args = ["tar", "-C", cookbook_base, "-czf", tarball_location, cookbook_name]
+ Chef::Log.debug("Tarball for #{cookbook_name} not found, so creating at #{tarball_location} with '#{args.join(' ')}'")
FileUtils.mkdir_p(Chef::Config.cookbook_tarball_path)
- FileUtils.rm_f(tarball_path)
- FileUtils.mv(file.path, tarball_path)
+ system(*args)
end
-
+ tarball_location
+ end
+
+ def expand_tarball_and_put_in_repository(cookbook_name, file)
+ # untar cookbook tarball into tempdir
+ tempdir = File.join("#{file.path}.data")
+ Chef::Log.debug("Creating #{tempdir} and untarring #{file.path} into it")
+ FileUtils.mkdir_p(tempdir)
+ raise "Could not untar file" unless system("tar", "xzf", file.path, "-C", tempdir)
+
+ cookbook_path = cookbook_location(cookbook_name)
+ tarball_path = cookbook_tarball_location(cookbook_name)
+
+ # clear any existing cookbook components and move tempdir into the repository
+ Chef::Log.debug("Moving #{tempdir} to #{cookbook_path}")
+ FileUtils.rm_rf(cookbook_path)
+ FileUtils.mkdir_p(cookbook_path)
+ Dir[File.join(tempdir, cookbook_name, "*")].each{|e| FileUtils.mv(e, cookbook_path)}
+
+ # clear the existing tarball (if exists) and move the downloaded tarball to the cache
+ Chef::Log.debug("Moving #{file.path} to #{tarball_path}")
+ FileUtils.mkdir_p(Chef::Config.cookbook_tarball_path)
+ FileUtils.rm_f(tarball_path)
+ FileUtils.mv(file.path, tarball_path)
end
+
end
end
diff --git a/chef-server-api/bin/chef-server b/chef-server-api/bin/chef-server
index f0c21de8af..173d29080f 100755
--- a/chef-server-api/bin/chef-server
+++ b/chef-server-api/bin/chef-server
@@ -9,9 +9,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,42 +20,34 @@
# Based on the 'merb' command, by Ezra
-# Add chef and chef-server-slice lib dirs to the load path
-# Load chef and chef-server slice from source rather than gem, if present
-
require "rubygems"
require "merb-core"
-CHEF_SERVER_VERSION = "0.8.11"
-
+# Load chef and chef-server-api from source rather than gem, if present
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../../chef/lib/chef'))
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
require 'chef'
require 'chef-server-api'
-# [ 'chef', 'chef-server-api' ].each do |lib|
-# $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib")))
-# library = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", lib, "lib", "#{lib}.rb"))
-# require library if File.exists?(library)
-# end
-#
+
# Ensure the chef gem we load is the same version as the chef server
unless defined?(Chef)
- gem "chef", "=" + CHEF_SERVER_VERSION
- require 'chef'
+ gem "chef", "=" + CHEF_SERVER_VERSION
+ require 'chef'
end
-Dir.chdir File.join(File.dirname(__FILE__),"..")
-#__DIR__ = Dir.getwd
-
+# Print the help message if our argv looks fishy
if ARGV[0] && ARGV[0] =~ /^[^-]/
ARGV.push "-H"
-end
+end
+
+# Default to thin
unless %w[-a --adapter -i --irb-console -r --script-runner].any? { |o| ARGV.index(o) }
ARGV.push *%w[-a thin]
end
-#ARGV.push *[ "-I", File.join(__DIR__, "config", "init.rb") ]
-#ARGV.push *[ "-m", __DIR__]
+
+# Tell merb where to root
+ARGV.push *[ "-m", CHEF_SERVER_API_ROOT]
# attempt to find the user and group that this process will become. Fixes CHEF-1015
if (index = ARGV.index("-u"))
@@ -65,6 +57,7 @@ if (index = ARGV.index("-G"))
Chef::Config[:signing_ca_group] = ARGV[index+1]
end
+# Find and load the config
if index = ARGV.index("-C")
config = ARGV[index+1]
ARGV.delete("-C")
@@ -76,7 +69,7 @@ else
)
end
-Chef::Log.init(Chef::Config[:log_location])
+Chef::Log.init(Chef::Config[:log_location])
Chef::Log.level = Chef::Config[:log_level]
Merb.start
diff --git a/chef-server-api/config/environments/development.rb b/chef-server-api/config/environments/development.rb
index 0a2ee75651..863408eb5f 100644
--- a/chef-server-api/config/environments/development.rb
+++ b/chef-server-api/config/environments/development.rb
@@ -2,3 +2,5 @@ Merb::Config.use do |c|
c[:exception_details] = true
c[:reload_classes] = true
end
+
+Merb.logger.level = :debug \ No newline at end of file
diff --git a/chef-server-api/config/rack.rb b/chef-server-api/config/rack.rb
new file mode 100644
index 0000000000..ec4df00bf3
--- /dev/null
+++ b/chef-server-api/config/rack.rb
@@ -0,0 +1,5 @@
+# Correctly set a content length.
+use Rack::ContentLength
+
+# this is our main merb application
+run Merb::Rack::Application.new
diff --git a/chef-server-api/config/router.rb b/chef-server-api/config/router.rb
index 72a284259f..709a83b07e 100644
--- a/chef-server-api/config/router.rb
+++ b/chef-server-api/config/router.rb
@@ -1,5 +1,3 @@
-# This file is here so slice can be testing as a stand alone application.
-
Merb::Router.prepare do
resources :users
diff --git a/chef-server-api/lib/chef-server-api.rb b/chef-server-api/lib/chef-server-api.rb
index e69de29bb2..a61272273f 100644
--- a/chef-server-api/lib/chef-server-api.rb
+++ b/chef-server-api/lib/chef-server-api.rb
@@ -0,0 +1,2 @@
+CHEF_SERVER_VERSION = "0.8.11"
+CHEF_SERVER_API_ROOT = File.expand_path(File.dirname(__FILE__) + '/../') \ No newline at end of file