Skip to content

Commit

Permalink
#7 extend .require
Browse files Browse the repository at this point in the history
  • Loading branch information
gutenye committed Jul 30, 2011
1 parent 2ffdcb8 commit 7890835
Show file tree
Hide file tree
Showing 18 changed files with 761 additions and 101 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*~
Gemfile.lock
tags
14 changes: 8 additions & 6 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
source :rubygems
source "http://rubygems.org"

gem "hike", "~>1.2.0"

group :development do
gem 'rspec'
gem 'watchr'
gem 'rag'
gem 'pd'
gem "rspec"
gem "watchr"
gem "rag"
gem "pd"
end

gemspec
#gemspec
67 changes: 49 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ Features
Introduction
-------------

The three levels of configuration include system, user, and cmdline:
The three levels of configuration include system, user, and realtime:

APP/lib/guten/rc.rb # system level
~/.gutenrc # user level
$ guten --list or ENV[GEMFILE]=x guten # cmdline level
/etc/foo or APP/lib/foo/rc.rb # system level
~/.foorc # user level
$ foo --list or $ ENV[GEMFILE]=x foo # realtime level

module Guten
Rc = Optimism.require("guten/rc") + Optimism.require("~/.gutenrc") # require use $:
Rc = Optimism.require(%w[foo/rc ~/.foorc])
Rc.list = true or Rc.gemfile = ENV[GEMFILE] # from cmdline.
end

Expand All @@ -41,7 +41,7 @@ The three levels of configuration include system, user, and cmdline:
my.development do # namespace
adapter "postgresql"
database "hello_development"
username "guten"
username "foo"
end

time proc{|offset| Time.now} # computed attribute
Expand All @@ -57,26 +57,26 @@ The three levels of configuration include system, user, and cmdline:
my.development do |c|
c.adapter = "mysql2"
c.database = "hello"
c.username = "guten"
c.username = "foo"
end

c.time = proc{|offset| Time.now}
end

### An example of some sugar syntax. _works in a file only_ ###

# file: guten/rc.rb
# file: foo/rc.rb
development:
adapter "mysql2"
database "hello"
username "guten"
username "foo"

#=>

development do
adapter "mysql2"
database "hello"
username "guten"
username "foo"
end


Expand All @@ -88,14 +88,17 @@ In order for this to work, a tab ("\t") must be used for indention.
In order to initialize the configuration object either of the two ways can be used.

Rc = Optimism.new
Rc = Optimism.require "guten/rc" # from file
Rc = Optimism.require "foo/rc" # from file
Rc = Optimism do
a 1
end
Rc = Optimism[a: 1] # from a hash data
Rc._merge!(a: 1)

file: "guten/rc.rb"
Rc = Optimism.new
Rc.production << {a: 1} #=> Rc.production.a is 1
Rc.production << Optimism.require_string("my.age = 1") #=> Rc.production.my.age is 1

file: "foo/rc.rb"

a 1

Expand Down Expand Up @@ -231,6 +234,28 @@ Internal, datas are stored as a Hash. You can access all hash methods via `_meth

Rc._keys #=> [:a]

### Require ###

# load configuration from file. support $:
Optimism.require %w(
foo/rc
~/.foorc
end

# load configuration from string
Optimism.require_string <<-EOF
my.age = 1
EOF


# load configuration from environment variable
ENV[OPTIMISM_A_B] = 1
Rc = Optimism.require_env(/OPTIMISM_(.*)/) #=> Rc.a_b is 1
Rc = Optimism.require_env(/OPTIMISM_(.*)/, split: '_') #=> Rc.a.b is 1

# load configuration from user input
Rc = Optimism.require_input("what's your name?", "my.name") #=> Rc.my.name is 'what you typed in terminal'

### Temporarily change ###

Rc.a = 1
Expand Down Expand Up @@ -259,22 +284,28 @@ Note: for a list of blocked methods, see Optimism::BUILTIN_METHODS
end
end

Optimism do
_.name = "foo"
my.name = "bar" # _ is optional here.
end

\# file: a.rb

c = self
c.host = "localhost"
c.port = 8080
c.name do |c|
_.host = "localhost"
_.port = 8080
_.name do |c|
c.first = "Guten"
end

Contributing
-------------
------------

* Feel free to join the project and make contributions (by submitting a pull request)
* Submit any bugs/features/ideas to github issue tracker
* Coding Style Guide: https://gist.github.com/1105334

[a list of Contributors](https://github.com/GutenYe/optimism/wiki/Contributors)

Install
----------

Expand Down
94 changes: 48 additions & 46 deletions lib/optimism.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
require 'hike'

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

%w(
semantics
hash_method_fix
parser
require
).each { |n| require "optimism/#{n}" }

# <#Optimism> is a node, it has _child, _parent and _root attribute.
Expand Down Expand Up @@ -34,14 +37,16 @@
class Optimism
autoload :VERSION, "optimism/version"

Error = Class.new Exception
LoadError = Class.new Error
Error = Class.new Exception
MissingFile = Class.new Error

BUILTIN_METHODS = [ :p, :raise, :sleep, :rand, :srand, :exit, :require, :at_exit, :autoload, :open]

class << self
public *BUILTIN_METHODS

include Require

# eval a file/string configuration.
#
# @params [String] content
Expand All @@ -53,7 +58,30 @@ def eval(content=nil, &blk)
optimism._root
end

# convert Hash, Optimism to Optimism
# deep convert Hash to optimism
#
# @example
# hash2optimism({a: {b: 1})
# #=> Optimism[a: Optimism[b: 1]]
#
# @param [Hash,Optimism] hash
# @return [Optimism]
def convert(hash)
return hash if Optimism === hash

node = Optimism.new
hash.each { |k,v|
if Hash === v
node[k] = convert(v)
else
node[k] = v
end
}
node
end

# convert Hash to Optimism
#
# @param [Optimism,Hash] data
# @return [Optimism]
def [](data)
Expand All @@ -80,55 +108,13 @@ def get(obj)
end
end

# load a configuration file,
# use $: and support '~/.gutenrc'
#
# @example
# Rc = Optimism.require("~/.gutenrc")
#
# Rc = Optimism.require("/absolute/path/rc.rb")
#
# Rc = Optimism.require("guten/rc") #=> load 'APP/lib/guten/rc.rb'
# # first try 'guten/rc.rb', then 'guten/rc'
#
# @param [String] name
# @return [Optimism]
def require(name)
path = nil

# ~/.gutenrc
if name =~ /^~/
file = File.expand_path(name)
path = file if File.exists?(file)

# /absolute/path/to/rc
elsif File.absolute_path(name) == name
path = name if File.exists?(name)

# relative/rc
else
catch :break do
$:.each { |p|
['.rb', ''].each { |ext|
file = File.join(p, name+ext)
if File.exists? file
path = file
throw :break
end
}
}
end
end

raise LoadError, "can't find file -- #{name}" unless path

Optimism.eval File.read(path)
end
end

undef_method *BUILTIN_METHODS
include Semantics
include HashMethodFix
include RequireInstanceMethod

# parent node, a <#Optimism>
attr_accessor :_parent
Expand Down Expand Up @@ -176,9 +162,25 @@ def _child=(obj)
end

# set data
#
# @example
#
# o = Optimism.new
#
# a = Optimism do
# _.b = 1
# end
#
# o[:a] = a OR o.a << a #=> o.a.b is 1
#
def []=(key, value)
key = key.respond_to?(:to_sym) ? key.to_sym : key

if Optimism === value
value._parent = self
value._root = self._root
end

@_child[key] = value
end

Expand Down
59 changes: 52 additions & 7 deletions lib/optimism/hash_method_fix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,68 @@ class Optimism
#
module HashMethodFix

# merge new data IN PLACE
# deep merge new data IN PLACE
#
# @params [Hash,Optimism] obj
# @return [self]
def _merge! obj
_child.merge! Optimism.get(obj)
def _merge!(other)
other = Optimism.convert(other)
other._each { |k, v|
if Optimism === self[k] and Optimism === other[k]
self[k]._merge!(other[k])
else
self[k] = other[k]
end
}

self
end

# merge new data
alias << _merge!

# deep merge new data
#
# @params [Hash,Optimism] obj
# @return [Optimism] new <#Optimism>
def _merge obj
data = _child.merge(Optimism.get(obj))
Optimism[data]
def _merge other
target = dup
other = Optimism.convert(other)

other._each { |k, v|
if Optimism === target[k] and Optimism === other[k]
target[k]._merge(other[k])
else
target[k] = other[k]
end
}

target
end

# support path
#
# @example
#
# o = Optimism do
# _.a = 1
# _.b.c = 2
# end
#
# o._get("b.c") #=> 2
# o._get("c.d") #=> nil. path doesn't exist.
# o._get("a.b") #=> nil. path is wrong
#
# @param [String] key
# @return [Object] value
def _get(key)
value = self
key.split(".").each { |k|
return nil unless Optimism === value # wrong path
value = value[k]
return nil if value.nil? # path doesn't exist.
}

value
end
end
end
Loading

2 comments on commit 7890835

@anithri
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are a coding machine! Nice job.

@gutenye
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks.

Please sign in to comment.