This commit is contained in:
Luca Guidi 2016-09-22 17:48:58 +02:00
parent 2d25764e7f
commit 0dc62d6c64
17 changed files with 549 additions and 41 deletions

2
.rspec Normal file
View File

@ -0,0 +1,2 @@
--color
--require spec_helper

8
.rubocop.yml Normal file
View File

@ -0,0 +1,8 @@
AllCops:
DisplayCopNames: true
DisplayStyleGuide: true
ExtraDetails: false
Style/StringLiterals:
Enabled: false
Metrics/LineLength:
Enabled: false

10
Gemfile
View File

@ -1,7 +1,7 @@
source 'https://rubygems.org'
gemspec
if !ENV['TRAVIS']
unless ENV['TRAVIS']
gem 'byebug', require: false, platforms: :mri
gem 'pry', require: false, platforms: :jruby
gem 'yard', require: false
@ -31,7 +31,7 @@ platforms :jruby do
gem 'jdbc-sqlite3'
end
gem 'coveralls', require: false
gem 'dotenv', '~> 2.0'
gem 'shotgun', '~> 0.9'
gem 'dotenv', '~> 2.0', require: false
gem 'shotgun', '~> 0.9', require: false
gem 'rubocop', '~> 0.43.0', require: false
gem 'coveralls', require: false

View File

@ -1,18 +1,14 @@
require 'rake'
require 'rake/testtask'
require 'bundler/gem_tasks'
require 'rspec/core/rake_task'
Rake::TestTask.new do |t|
t.pattern = 'test/**/*_test.rb'
t.libs.push 'test'
end
RSpec::Core::RakeTask.new(:spec)
namespace :test do
task :coverage do
ENV['COVERAGE'] = 'true'
Rake::Task['test'].invoke
Rake::Task['spec'].invoke
end
end
task default: :test
task default: :spec

View File

@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
spec.version = Hanami::VERSION
spec.authors = ['Luca Guidi', 'Trung Lê', 'Alfonso Uceda Pompa']
spec.email = ['me@lucaguidi.com', 'trung.le@ruby-journal.com', 'uceda73@gmail.com']
spec.summary = %q{The web, with simplicity.}
spec.description = %q{Hanami is a web framework for Ruby}
spec.summary = 'The web, with simplicity'
spec.description = 'Hanami is a web framework for Ruby'
spec.homepage = 'http://hanamirb.org'
spec.license = 'MIT'
@ -17,7 +17,9 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test)/})
spec.require_paths = ['lib']
spec.required_ruby_version = '>= 2.2.0'
spec.required_ruby_version = '>= 2.3.0'
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
spec.add_dependency 'hanami-utils', '~> 0.8'
spec.add_dependency 'hanami-validations', '~> 0.6'
@ -28,10 +30,9 @@ Gem::Specification.new do |spec|
spec.add_dependency 'hanami-mailer', '~> 0.3'
spec.add_dependency 'hanami-assets', '~> 0.3'
spec.add_dependency 'thor', '~> 0.19'
spec.add_dependency 'bundler', '~> 1.6'
spec.add_dependency 'bundler', '~> 1.13'
spec.add_development_dependency 'minispec-metadata', '~> 3.2.1'
spec.add_development_dependency 'minitest', '~> 5'
spec.add_development_dependency 'rack-test', '~> 0.6'
spec.add_development_dependency 'rake', '~> 10'
spec.add_development_dependency 'rspec', '~> 3.5'
spec.add_development_dependency 'rack-test', '~> 0.6'
spec.add_development_dependency 'rake', '~> 11.3'
end

65
lib/hanami/env.rb Normal file
View File

@ -0,0 +1,65 @@
begin
require 'dotenv'
rescue LoadError # rubocop:disable Lint/HandleExceptions
end
module Hanami
# Encapsulate access to ENV
#
# @since x.x.x
# @api private
class Env
# Create a new instance
#
# @param env [#[],#[]=] a Hash like object. It defaults to ENV
#
# @return [Hanami::Env]
#
# @since x.x.x
# @api private
def initialize(env: ENV)
@env = env
end
# Return a value, if found
#
# @param key [String] the key
#
# @return [String,NilClass] the value, if found
#
# @since x.x.x
# @api private
def [](key)
@env[key]
end
# Sets a value
#
# @param key [String] the key
# @param value [String] the value
#
# @since x.x.x
# @api private
def []=(key, value)
@env[key] = value
end
# Loads a dotenv file and updates self
#
# @param path [String, Pathname] the path to the dotenv file
#
# @return void
#
# @since x.x.x
# @api private
def load!(path)
return unless defined?(Dotenv)
contents = ::File.open(path, "rb:bom|utf-8", &:read)
parsed = Dotenv::Parser.call(contents)
@env.merge!(parsed)
nil
end
end
end

View File

@ -2,11 +2,8 @@ require 'thread'
require 'pathname'
require 'hanami/utils'
require 'hanami/utils/hash'
require 'hanami/env'
require 'hanami/hanamirc'
begin
require 'dotenv'
rescue LoadError
end
module Hanami
# Define and expose information about the Hanami environment.
@ -196,6 +193,7 @@ module Hanami
# # the one defined in the parent (eg `FOO` is overwritten). All the
# # other settings (eg `XYZ`) will be left untouched.
def initialize(options = {})
@env = Hanami::Env.new(env: options.delete(:env) || ENV)
@options = Hanami::Hanamirc.new(root).options
@options.merge! Utils::Hash.new(options.clone).symbolize!
LOCK.synchronize { set_env_vars! }
@ -218,7 +216,7 @@ module Hanami
#
# @see Hanami::Environment::DEFAULT_ENV
def environment
@environment ||= ENV[HANAMI_ENV] || rack_env || DEFAULT_ENV
@environment ||= env[HANAMI_ENV] || rack_env || DEFAULT_ENV
end
# @since 0.3.1
@ -304,9 +302,9 @@ module Hanami
# @see Hanami::Environment::DEFAULT_HOST
# @see Hanami::Environment::LISTEN_ALL_HOST
def host
@host ||= @options.fetch(:host) {
ENV[HANAMI_HOST] || default_host
}
@host ||= @options.fetch(:host) do
env[HANAMI_HOST] || default_host
end
end
# The HTTP port
@ -324,7 +322,9 @@ module Hanami
#
# @see Hanami::Environment::DEFAULT_PORT
def port
@port ||= @options.fetch(:port) { ENV[HANAMI_PORT] || DEFAULT_PORT }.to_i
@port ||= @options.fetch(:port) do
env[HANAMI_PORT] || DEFAULT_PORT
end.to_i
end
# Path to the Rack configuration file
@ -401,10 +401,10 @@ module Hanami
# @since 0.4.0
# @api private
def architecture
@options.fetch(:architecture) {
@options.fetch(:architecture) do
puts "Cannot recognize Hanami architecture, please check `.hanamirc'"
exit 1
}
end
end
# @since 0.4.0
@ -416,7 +416,7 @@ module Hanami
# @since 0.6.0
# @api private
def serve_static_assets?
SERVE_STATIC_ASSETS_ENABLED == ENV[SERVE_STATIC_ASSETS]
SERVE_STATIC_ASSETS_ENABLED == env[SERVE_STATIC_ASSETS]
end
# @since 0.6.0
@ -463,6 +463,8 @@ module Hanami
private
attr_reader :env
# @since 0.1.0
# @api private
def set_env_vars!
@ -473,16 +475,18 @@ module Hanami
# @since 0.2.0
# @api private
def set_hanami_env_vars!
ENV[HANAMI_ENV] = ENV[RACK_ENV] = environment
ENV[HANAMI_HOST] = host
ENV[HANAMI_PORT] = port.to_s
env[HANAMI_ENV] = env[RACK_ENV] = environment
env[HANAMI_HOST] = host
env[HANAMI_PORT] = port.to_s
end
# @since 0.2.0
# @api private
def set_application_env_vars!
return unless defined?(Dotenv) && (dotenv = root.join(DEFAULT_DOTENV_ENV % environment)).exist?
Dotenv.overload dotenv
dotenv = root.join(DEFAULT_DOTENV_ENV % environment)
return unless dotenv.exist?
env.load!(dotenv)
end
# @since 0.1.0
@ -494,11 +498,11 @@ module Hanami
# @since 0.6.0
# @api private
def rack_env
case ENV[RACK_ENV]
case env[RACK_ENV]
when RACK_ENV_DEPLOYMENT
PRODUCTION_ENV
else
ENV[RACK_ENV]
env[RACK_ENV]
end
end
end

View File

@ -2,5 +2,5 @@ module Hanami
# Defines the full version
#
# @since 0.1.0
VERSION = '0.8.0'.freeze
VERSION = '1.0.0.alpha1'.freeze
end

30
spec/env_spec.rb Normal file
View File

@ -0,0 +1,30 @@
RSpec.describe Hanami::Env do
after do
ENV['HANAMI_ENV_TEST_VARIABLE'] = nil
end
describe "#[]" do
it "reads value from ENV" do
expect(described_class.new['PATH']).to eq(ENV['PATH'])
end
end
describe "#[]=" do
it "sets value to ENV" do
subject = described_class.new
subject['HANAMI_ENV_TEST_VARIABLE'] = 'foo'
expect(subject['HANAMI_ENV_TEST_VARIABLE']).to eq(ENV['HANAMI_ENV_TEST_VARIABLE'])
end
end
describe "#load!" do
it "loads env vars" do
env = {}
subject = described_class.new(env: env)
subject.load!('spec/fixtures/dotenv/.env.development')
expect(subject['BAZ']).to eq('yes')
end
end
end

308
spec/environment_spec.rb Normal file
View File

@ -0,0 +1,308 @@
RSpec.describe Hanami::Environment do
let(:env) { Hash[] }
let(:default_development_env) do
Hash[
'RACK_ENV' => 'development',
'HANAMI_ENV' => 'development',
'HANAMI_HOST' => 'localhost',
'HANAMI_PORT' => '2300'
]
end
let(:default_test_env) do
Hash[
'RACK_ENV' => 'test',
'HANAMI_ENV' => 'test',
'HANAMI_HOST' => '0.0.0.0',
'HANAMI_PORT' => '2300'
]
end
describe "#initialize" do
context "global .env" do
it "doesn't set env vars from .env" do
with_directory('spec/fixtures') do
described_class.new(env: env)
expect(env['FOO']).to be_nil # see spec/fixtures/.env
end
end
it "doesn't sets port" do
with_directory('spec/fixtures') do
subject = described_class.new(env: env)
# returns default instead the value from spec/fixtures/.env
expect(subject.port).to eq(2300)
end
end
end
context "per environment .env" do
it "sets env vars from .env.development" do
with_directory('spec/fixtures/dotenv') do
described_class.new(env: env)
expect(env['HANAMI_PORT']).to eq('42')
expect(env['BAZ']).to eq('yes')
expect(env['WAT']).to eq('true')
end
end
it "sets port from .env.development" do
with_directory('spec/fixtures/dotenv') do
subject = described_class.new(env: env)
expect(subject.port).to eq(42)
end
end
end
context "missing per environment .env" do
it "doesn't alter env" do
with_directory('spec/fixtures/nodotenv') do
described_class.new(env: env)
expect(env).to eq(default_development_env)
end
end
end
context "when the .env for the current environment is missing" do
let(:env) { Hash['HANAMI_ENV' => 'test'] }
it "doesn't set env vars" do
with_directory('spec/fixtures/dotenv') do
described_class.new(env: env)
expect(env).to eq(default_test_env)
end
end
end
end # initialize
describe "#environment" do
context "when HANAMI_ENV isn't set" do
it "returns 'development'" do
subject = described_class.new(env: env)
expect(subject.environment).to eq('development')
end
end
context "when HANAMI_ENV is set" do
let(:env) { Hash['HANAMI_ENV' => 'test'] }
it "returns that value" do
subject = described_class.new(env: env)
expect(subject.environment).to eq('test')
end
end
context "when RACK_ENV is set to 'production'" do
let(:env) { Hash['RACK_ENV' => 'production'] }
it "returns that value" do
subject = described_class.new(env: env)
expect(subject.environment).to eq('production')
end
end
context "when RACK_ENV is set to 'deployment'" do
let(:env) { Hash['RACK_ENV' => 'deployment'] }
it "returns 'production'" do
subject = described_class.new(env: env)
expect(subject.environment).to eq('production')
end
end
context "when both HANAMI_ENV and RACK_ENV are set" do
let(:env) { Hash['HANAMI_ENV' => 'test', 'RACK_ENV' => 'production'] }
it "gives precedence to HANAMI_ENV" do
subject = described_class.new(env: env)
expect(subject.environment).to eq('test')
end
end
context "when env vars are changed from different process" do
it "doesn't change value after initialization" do
subject = described_class.new(env: env)
expect(subject.environment).to eq('development')
env['HANAMI_ENV'] = 'production'
expect(subject.environment).to eq('development')
end
end
end # environment
describe "#environment?" do
subject { described_class.new(env: env) }
context "when matched" do
let(:env) { Hash['HANAMI_ENV' => 'test'] }
context "with single name" do
it "returns true" do
expect(subject.environment?(:test)).to be(true)
expect(subject.environment?('test')).to be(true)
end
end
context "with multiple names" do
it "returns true" do
expect(subject.environment?(:development, :test)).to be(true)
expect(subject.environment?('development', 'test')).to be(true)
end
end
end
context "when not matched" do
let(:env) { Hash['HANAMI_ENV' => 'development'] }
context "with single name" do
it "returns false" do
expect(subject.environment?(:test)).to be(false)
expect(subject.environment?('test')).to be(false)
end
end
context "with multiple names" do
it "returns false" do
expect(subject.environment?(:test, :production)).to be(false)
expect(subject.environment?('test', 'production')).to be(false)
end
end
end
end # environment?
describe "#bundler_groups" do
it "returns a set of groups for Bundler" do
subject = described_class.new(env: env)
expect(subject.bundler_groups).to eq([:default, subject.environment])
end
end # bundler_groups
describe "#host" do
context "when not specified" do
context "and default env" do
it "returns 'localhost'" do
subject = described_class.new(env: env)
expect(subject.host).to eq('localhost')
end
end
context "and other env" do
let(:env) { Hash['HANAMI_ENV' => 'test'] }
it "returns '0.0.0.0'" do
subject = described_class.new(env: env)
expect(subject.host).to eq('0.0.0.0')
end
end
end
context "when specified while initializing" do
it 'returns that value' do
subject = described_class.new(env: env, host: host = 'hanamirb.test')
expect(subject.host).to eq(host)
end
end
context "when HANAMI_HOST is set" do
let(:env) { Hash['HANAMI_HOST' => host] }
let(:host) { 'hanami.host' }
it 'returns that value' do
subject = described_class.new(env: env)
expect(subject.host).to eq(host)
end
end
context "when both the option and HANAMI_HOST are set" do
let(:env) { Hash['HANAMI_HOST' => 'hanami.host'] }
it 'returns that value' do
subject = described_class.new(env: env, host: host = 'hanamirb.org')
expect(subject.host).to eq(host)
end
end
end # host
describe "#port" do
context "when not specified" do
it "returns 2300" do
subject = described_class.new(env: env)
expect(subject.port).to eq(2300)
end
end
context "when specified while initializing" do
it 'returns that value' do
subject = described_class.new(env: env, port: port = 9292)
expect(subject.port).to eq(port)
end
end
context "when HANAMI_PORT is set" do
let(:env) { Hash['HANAMI_PORT' => port] }
let(:port) { 8080 }
it 'returns that value' do
subject = described_class.new(env: env)
expect(subject.port).to eq(port)
end
end
context "when both the option and HANAMI_PORT are set" do
let(:env) { Hash['HANAMI_PORT' => 8081] }
it 'returns that value' do
subject = described_class.new(env: env, port: port = 9393)
expect(subject.port).to eq(port)
end
end
end # port
describe "#project_name" do
it "equals to given value" do
subject = described_class.new(env: env, project: project = 'bookshelf')
expect(subject.project_name).to eq(project)
end
end # project_name
describe "#root" do
it "equals to Dir.pwd" do
subject = described_class.new(env: env)
expect(subject.root).to eq(Pathname(Dir.pwd))
end
end # root
describe "#rackup" do
it "is 'config.ru' at root" do
subject = described_class.new(env: env)
expect(subject.rackup).to eq(subject.root.join('config.ru'))
end
end # rackup
end

3
spec/fixtures/.env vendored Normal file
View File

@ -0,0 +1,3 @@
FOO="bar"
BAZ="no"
HANAMI_PORT=42

38
spec/fixtures/dotenv/.byebug_history vendored Normal file
View File

@ -0,0 +1,38 @@
c
n
env
n
dotenv
n
step
@env
@options
n
c
@options
n
@env
n
options
c
n
step
n
step
c
n
step
env
c
ENV['HANAMI_PORT']
ENV.keys
n
dotenv
n
Dotenv
step
@options
n
@env
n
step

3
spec/fixtures/dotenv/.env.development vendored Normal file
View File

@ -0,0 +1,3 @@
HANAMI_PORT=42
BAZ="yes"
WAT="true"

1
spec/fixtures/nodotenv/.gitkeep vendored Normal file
View File

@ -0,0 +1 @@
gitkeep

23
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,23 @@
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
config.filter_run_when_matching :focus
config.disable_monkey_patching!
config.warnings = true
config.default_formatter = 'doc' if config.files_to_run.one?
config.order = :random
Kernel.srand config.seed
end
Dir[__dir__ + '/support/**/*.rb'].each { |f| require_relative f }
require 'hanami'

View File

@ -0,0 +1,21 @@
require 'pathname'
module RSpec
module WithDirectory
private
def with_directory(directory)
current = Dir.pwd
target = Pathname.new(Dir.pwd).join(directory)
Dir.chdir(target)
yield
ensure
Dir.chdir(current)
end
end
end
RSpec.configure do |config|
config.include RSpec::WithDirectory
end

5
spec/version_spec.rb Normal file
View File

@ -0,0 +1,5 @@
RSpec.describe Hanami::VERSION do
it 'returns current version' do
expect(subject).to eq('1.0.0.alpha1')
end
end