open_dsl 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Envato, Ian Leitch.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,70 @@
1
+ = Open DSL
2
+
3
+ Open DSL is a DSL (Domain Specific Language) builder which aims to provide a highly readable DSL and the flexibility to integrate with existing Classes in your system. Open DSL uses OpenStructs internally when creating collections of attributes.. hence the name :)
4
+
5
+ == Basic Example
6
+
7
+ open_dsl do
8
+ Finance do
9
+ tax do
10
+ rate "17.5%"
11
+ end
12
+
13
+ accounts do
14
+ Account do
15
+ name "earnings"
16
+ withdrawal_limit 100_00
17
+ end
18
+
19
+ Account do
20
+ name "deposits"
21
+ withdrawal_limit 0
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ Notice that Finance and Account start with an upper-case character, just like Ruby classes. This tells Open DSL that if an existing class with the same name exists then use it, otherwise a new class of that name will be created for you. This allows you to use Open DSL on classes that already exist in your system, the advantage of this is that you can use Open DSL as a way to configure a class whilst keeping business logic and functional code separate.
28
+
29
+ In the case of Account, the class instance is assigned to the Finance instance with a name inferred from the Account class; an attribute named "account" is created which contains an instance of Account. "tax" will result in an OpenStruct being created and the "rate" attribute assigned. "accounts" is plural and therefore signifies an array containing each account.
30
+
31
+ == Installing & Usage
32
+
33
+ gem install open_dsl
34
+
35
+ require 'rubygems'
36
+ require 'open_dsl'
37
+
38
+ open_dsl do
39
+ end
40
+ or
41
+ OpenDsl::Builder.build do
42
+ end
43
+
44
+ == Advanced Examples
45
+
46
+ === Explicit Attribute Names for Existing Classes
47
+
48
+ If you have an existing class but like to assign it to an attribute with a different name or Open DSL fails to infer a nice name for it, you can explicity specify a name:
49
+
50
+ open_dsl do
51
+ configuration(MyConfigurationClass) do
52
+ value_1 "foo"
53
+ value_2 "bar"
54
+ end
55
+ end
56
+
57
+ == Compatibility
58
+
59
+ Tested with Ruby 1.8.7 only so far.
60
+
61
+ == Contributions
62
+
63
+ * Fork the project.
64
+ * Make your feature addition or bug fix.
65
+ * Add tests for it, specs live in the specs/ directory.
66
+ * Commit and send me a pull request.
67
+
68
+ == Copyright
69
+
70
+ Copyright (c) 2010 Envato, Ian Leitch. See LICENSE for details.
@@ -0,0 +1,35 @@
1
+ module OpenDsl
2
+ class Builder
3
+ def self.build(file = nil, &blk)
4
+ builder = new(file, &blk)
5
+ builder.context.toplevel_object
6
+ end
7
+
8
+ def initialize(file = nil, &blk)
9
+ if file
10
+ build_from_file(file)
11
+ elsif blk
12
+ build_from_proc(blk)
13
+ else
14
+ raise "OpenDsl#new requires either a file to load or a block as an argument"
15
+ end
16
+ end
17
+
18
+ def context
19
+ @context
20
+ end
21
+
22
+ protected
23
+
24
+ def build_from_file(file)
25
+ end
26
+
27
+ def build_from_proc(blk)
28
+ instance_eval(&blk)
29
+ end
30
+
31
+ def method_missing(const_name, *args, &blk)
32
+ @context = Context.new(const_name, &blk)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,92 @@
1
+ module OpenDsl
2
+ class Context
3
+ include StringHelpers
4
+ attr_reader :toplevel_object
5
+
6
+ def initialize(const_name, &blk)
7
+ raise "Expected a constant name starting with an upper-case character, got '#{const_name}'" unless constant_or_constant_name?(const_name)
8
+ @toplevel_object = new_instance(const_name)
9
+ @stack = EvalStack.new(self)
10
+ @stack.eval_and_keep(@toplevel_object, &blk)
11
+ end
12
+
13
+ protected
14
+
15
+ def method_missing(name, *args, &blk)
16
+ # TODO: detect an internal error?
17
+
18
+ value = args.first
19
+ if constant_or_constant_name?(name)
20
+ assign_constant(name, &blk)
21
+ elsif blk
22
+ if constant_or_constant_name?(value)
23
+ assign_constant_to_explicit_attribute(name, value, &blk)
24
+ else
25
+ if plural?(name)
26
+ assign_collection(name, &blk)
27
+ else
28
+ assign_attribute_with_block(name, &blk)
29
+ end
30
+ end
31
+ else
32
+ assign_attribute(name, value)
33
+ end
34
+ end
35
+
36
+ def assign_constant(name, &blk)
37
+ instance = new_instance(name)
38
+ assign_attribute(attribute_name(name), instance)
39
+ @stack.eval(instance, &blk)
40
+ end
41
+
42
+ def assign_constant_to_explicit_attribute(name, const, &blk)
43
+ instance = new_instance(const)
44
+ assign_attribute(name, instance)
45
+ @stack.eval(instance, &blk)
46
+ end
47
+
48
+ def assign_attribute_with_block(name, &blk)
49
+ struct = OpenStruct.new
50
+ assign_attribute(name, struct)
51
+ @stack.eval(struct, &blk)
52
+ end
53
+
54
+ def assign_collection(name, &blk)
55
+ array = Array.new
56
+ assign_attribute(name, array)
57
+ @stack.eval(array, &blk)
58
+ end
59
+
60
+ def assign_attribute(name, value)
61
+ if @stack.bottom.kind_of?(Array)
62
+ @stack.bottom << value
63
+ else
64
+ define_getter_and_setter_if_needed(name)
65
+ @stack.bottom.send("#{name}=", value)
66
+ end
67
+ end
68
+
69
+ def define_getter_and_setter_if_needed(name)
70
+ return if name.respond_to?("#{name}=")
71
+
72
+ @stack.bottom.class.instance_eval do
73
+ define_method("#{name}=") do |value|
74
+ instance_variable_set("@#{name}", value)
75
+ end
76
+
77
+ define_method(name) do
78
+ instance_variable_get("@#{name}")
79
+ end
80
+ end
81
+ end
82
+
83
+ def new_instance(const_name)
84
+ get_or_define_const(const_name).new
85
+ end
86
+
87
+ def get_or_define_const(name_or_const)
88
+ return name_or_const if name_or_const.is_a?(Class)
89
+ Object.const_defined?(name_or_const) ? Object.const_get(name_or_const) : Object.const_set(name_or_const, Class.new)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,22 @@
1
+ module OpenDsl
2
+ class EvalStack
3
+ def initialize(context_binding)
4
+ @stack = []
5
+ @context_binding = context_binding
6
+ end
7
+
8
+ def eval_and_keep(object, &blk)
9
+ @stack.push(object)
10
+ @context_binding.instance_eval(&blk)
11
+ end
12
+
13
+ def eval(object, &blk)
14
+ eval_and_keep(object, &blk)
15
+ @stack.pop
16
+ end
17
+
18
+ def bottom
19
+ @stack.last
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module OpenDsl
2
+ module StringHelpers
3
+ def constant_or_constant_name?(str_or_class)
4
+ return true if str_or_class.is_a?(Class)
5
+ !!(str_or_class.to_s =~ /^[A-Z]/)
6
+ end
7
+
8
+ def attribute_name(const_name)
9
+ const_name.to_s.gsub(/::/, '/').
10
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
11
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
12
+ tr("-", "_").
13
+ downcase
14
+ end
15
+
16
+ def plural?(str)
17
+ !!(str.to_s =~ /s$/)
18
+ end
19
+ end
20
+ end
data/lib/open_dsl.rb ADDED
@@ -0,0 +1,16 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'ostruct'
4
+
5
+ require 'open_dsl/string_helpers'
6
+ require 'open_dsl/builder'
7
+ require 'open_dsl/context'
8
+ require 'open_dsl/eval_stack'
9
+
10
+ module OpenDsl
11
+ VERSION = '0.1'
12
+ end
13
+
14
+ def open_dsl(&blk)
15
+ OpenDsl::Builder.build(&blk)
16
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: open_dsl
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Ian Leitch
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-11 00:00:00 +11:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ description: Open DSL is a DSL (Domain Specific Language) builder which aims to provide a highly readable DSL and the flexibility to integrate with existing Classes in your system. Open DSL uses OpenStructs internally when creating collections of attributes.. hence the name :)
35
+ email:
36
+ - port001@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - lib/open_dsl/builder.rb
45
+ - lib/open_dsl/context.rb
46
+ - lib/open_dsl/eval_stack.rb
47
+ - lib/open_dsl/string_helpers.rb
48
+ - lib/open_dsl.rb
49
+ - LICENSE
50
+ - README.rdoc
51
+ has_rdoc: true
52
+ homepage: http://github.com/ileitch/open_dsl
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ hash: 3
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 23
75
+ segments:
76
+ - 1
77
+ - 3
78
+ - 6
79
+ version: 1.3.6
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: A simple DSL library that extends your existing classes.
87
+ test_files: []
88
+