blockbuilder 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.komodoproject
19
+ .komodotools
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in blockbuilder.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Christopher Thornton
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,168 @@
1
+ BlockBuilder
2
+ ============
3
+ Make callbacks easily and build blocks with ease! BlockBuilder is intended to be used with more heavy weight method calls that might require intermediate callbacks. It's almost like creating classes on the fly!
4
+
5
+ ## Usage
6
+ Some code samples to get you started:
7
+
8
+ ### File Download Example
9
+ Downloading a file probably doesn't merit the block builder, but it demonstrates the concept of callbacks.
10
+
11
+ ```ruby
12
+ def download_file(url, &block)
13
+
14
+ # Configure your block here, along with defaults
15
+ builder = BlockBuilder.build block do
16
+
17
+ # Set optional default variables!
18
+ set :verbose, true
19
+
20
+ on_fail do |http_code, body|
21
+ puts "Oh no! HTTP download failed! Code: #{http_code}"
22
+ puts "Downloaded body: #{body}" if verbose
23
+ end
24
+
25
+ on_success do |body|
26
+ puts "Downloaded file Successfully!"
27
+ end
28
+ end
29
+
30
+ # Now execute your blocks! Note you don't have to explicitly
31
+ # define any of your callbacks.
32
+ result = download_some_file_from_internet(url)
33
+ if result.http_code == 200
34
+ builder.on_success.call result.body
35
+ else
36
+ builder.on_fail.call result.http_code, result.body
37
+ end
38
+
39
+ end
40
+
41
+
42
+ # Now you can call the method above and change the defaults!
43
+ download_file "example.com/exists.txt" do
44
+ on_success do |body|
45
+ File.open('some/file.txt', 'w') {|f| f.write body }
46
+ end
47
+ end
48
+
49
+ # Only outputs "Oh no! HTTP download failed! Code: 404"
50
+ download_file "example.com/doesnotexist.txt" do
51
+ set :verbose, false
52
+ end
53
+ ```
54
+
55
+ ### Callbacks and Remote Data
56
+ Sometimes you might connect to another service or API that contains critical information. And perhaps you need to query multiple times with this API to get the data you need. Callbacks can help with this!
57
+
58
+ ```ruby
59
+ As a convention, you should always document a block builder method and let consumers of
60
+ # this method know how to use it!
61
+ # @example
62
+ # # Here is a code sample on how to use this method, etc
63
+ def create_remote_user(username, password, &block)
64
+ builder = BlockBuilder.build block do
65
+ set :logger, Logger.new
66
+
67
+ # You can force the creation of a callback using the abstract operator. By default,
68
+ # without the abstract modifier, calling an undefined callback will simply do nothing
69
+ abstract :on_user_created
70
+ end
71
+
72
+ # If any callbacks label as 'abstract' aren't defined, then code execution will
73
+ # never reach this line.
74
+
75
+ # Here, you may first want to create the user and create a callback. This callback
76
+ # can then be used to quickly save an entry in the database before other operations
77
+ # are executed. This is useful, for example, if the server crashes during the middle
78
+ # of the request or there is a bug later in the method call. This way, at least you
79
+ # have the user information saved in your local database and you can retrieve the
80
+ # user information later.
81
+ result = api.create_user username, password
82
+ builder.on_user_created.call(result)
83
+
84
+ # Now we can keep on executing methods and have somewhere else take care of it. Using
85
+ # the set logger variable, we can allow someone to externally change the logger used.
86
+ #
87
+ # Now what if the connection timed out here? Or the server crashed? That's the point
88
+ # of the earlier callback! It allows you to save partial data without interfering
89
+ # with the internal logic of this method!
90
+ start = Time.now
91
+ result2 = api.some_very_time_intensive_call_involving(username)
92
+ builder.logger.info "Executed time intensive call in #{Time.now - start} sec!"
93
+ builder.on_finish_lengthy_task.call(result2)
94
+
95
+ # Another fun use for this: collecting statistics! It might be useful to collect a
96
+ # bunch of statistics for a particular task. However, not every consumer of your
97
+ # method will care for statistics. Just for fun, let's only calculate some
98
+ # statistics about this method call only if they care to calculate stats!
99
+ #
100
+ # We use 'builder.defined?' here because doing
101
+ #
102
+ # builder.generate_statistics.call(some_lengthy_statistic_compilation)
103
+ #
104
+ # will compile statistics, regardless of whether 'generate_statistics' is defined.
105
+ if builder.defined? :generate_statistics
106
+ stats = some_lengthy_statistic_compilation
107
+ builder.generate_statistics.call(stats)
108
+ end
109
+
110
+
111
+ # Now you can add any more callbacks as you see fit! And don't forget, you can still
112
+ # return some data from this method call if you want!
113
+ end
114
+ ```
115
+
116
+ Now you can use this method in multiple places. Here, assume this is where the code is primarily used:
117
+
118
+ ```ruby
119
+ create_remote_user 'user', 'password' do
120
+
121
+ # This easily lets us use our Rails logger
122
+ set :logger, Rails.logger
123
+
124
+ # Remember, our abstract callback was declared, so we must define it here. In this
125
+ # case, assume we have a Ruby on Rails model that deals with saving saved data
126
+ on_user_created do |result|
127
+ u = RemoteUser.new(result)
128
+ u.some_misc_operations
129
+ u.save!
130
+ end
131
+ end
132
+ ```
133
+
134
+ Finally, assume this is some benchmark suite, or something else:
135
+
136
+ ```ruby
137
+ # Assume this is some benchmark suite or testing file
138
+ create_remote_user 'user', 'password' do
139
+ on_user_created do |result|
140
+ # .. Implement here
141
+ end
142
+
143
+ # In this case, we might enjoy statistics
144
+ generate_statistics do |stats|
145
+ puts "Statistics for this operation:"
146
+ puts " * Total execution time: #{stats.exec_time}"
147
+ puts "..."
148
+ end
149
+
150
+ end
151
+ ```
152
+
153
+
154
+
155
+ ## Installation
156
+
157
+ Add this line to your application's Gemfile:
158
+
159
+ gem 'blockbuilder'
160
+
161
+ And then execute:
162
+
163
+ $ bundle
164
+
165
+ Or install it yourself as:
166
+
167
+ $ gem install blockbuilder
168
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'block_builder/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "blockbuilder"
8
+ gem.version = BlockBuilder::VERSION
9
+ gem.authors = ["Christopher Thornton"]
10
+ gem.email = ["rmdirbin@gmail.com"]
11
+ gem.description = %q{Make callbacks easily and build blocks with ease! BlockBuilder is intended to be used with more heavy weight method calls that might require intermediate callbacks. It's almost like creating classes on the fly!}
12
+ gem.summary = %q{Create complex function callbacks with ease!}
13
+ gem.homepage = "https://github.com/cgthornt/BlockBuilder"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,14 @@
1
+ require "block_builder/version"
2
+ require "block_builder/builder"
3
+ require "block_builder/attribute"
4
+
5
+ module BlockBuilder
6
+
7
+ class << self
8
+
9
+ def build(target_block, *options, &default_block)
10
+ return BlockBuilder::Builder.new(target_block, *options, &default_block)
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,46 @@
1
+ module BlockBuilder
2
+ class Attribute
3
+
4
+ attr_accessor :name, :block, :args, :parent_builder
5
+
6
+ def initialize(parent_builder, name, args = nil, block = nil)
7
+ @name = name
8
+ @args = args || []
9
+ @block = block
10
+ @parent_builder = parent_builder
11
+ end
12
+
13
+
14
+ #def set_block(some_block)
15
+ # block = some_block
16
+ # # builder._build(some_block) if some_block.is_a?(Proc)
17
+ #end
18
+
19
+ #def builder
20
+ # @builder = BlockBuilder::Builder.new(block) if @builder.nil?
21
+ # return @builder
22
+ #end
23
+
24
+ def call(*args)
25
+ block.call(*args) if block?
26
+ end
27
+
28
+ def block?
29
+ block.is_a?(Proc)
30
+ end
31
+
32
+ def blank?
33
+ args.empty? and !block?
34
+ end
35
+
36
+ def val; args.first; end
37
+ def value; val; end
38
+ def to_s; val; end
39
+
40
+ def vals; args; end
41
+ def values; vals; end
42
+
43
+
44
+
45
+ end
46
+ end
@@ -0,0 +1,69 @@
1
+ module BlockBuilder
2
+ class Builder
3
+
4
+ attr_reader :_target_block, :_symbol_table, :_options, :_config_table, :_abstract_list
5
+
6
+
7
+ def initialize(target_block, *args, &default_block)
8
+ @_target_block = target_block
9
+ options = {}
10
+ options = args.pop if args.last.is_a?(Hash)
11
+ args.each{|a| options[a] = true }
12
+ @_options = options
13
+ @_symbol_table = Hash.new
14
+ @_config_table = Hash.new
15
+ @_abstract_list = Array.new
16
+
17
+ # Default block, target block!
18
+ _build(default_block)
19
+ _build(target_block)
20
+
21
+ # Now call them out on abstractness!
22
+ _abstract_list.each do |method|
23
+ raise ArgumentError, "Abstract callback '#{method}' must be defined" unless _symbol_table.key?(method)
24
+ end
25
+ end
26
+
27
+
28
+ def _build(block)
29
+ instance_eval(&block) if block.is_a?(Proc)
30
+ end
31
+
32
+ def set(key, val)
33
+ _config_table[key] = val
34
+ end
35
+
36
+ # Define methods as abstract, so they must be implemented
37
+ def abstract(*methods)
38
+ @_abstract_list = _abstract_list | methods
39
+ end
40
+
41
+ def method_missing(method, *args, &block)
42
+ if _config_table.key?(method) and args.empty? and !block_given?
43
+ return _config_table[method]
44
+ end
45
+
46
+ method = method.to_s.chomp('=').to_sym if method.to_s[-1] == "="
47
+
48
+ attribute = _symbol_table[method]
49
+ if attribute.nil?
50
+ attribute = Attribute.new(self, method)
51
+ _symbol_table[method] = attribute
52
+ end
53
+
54
+ # Read attribute if no params
55
+ return attribute if args.empty? and !block_given?
56
+
57
+ attribute.args = args
58
+ attribute.block = block if block_given?
59
+
60
+ return attribute
61
+ end
62
+
63
+ def defined?(method_call)
64
+ _symbol_table.key?(method_call)
65
+ end
66
+
67
+
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module BlockBuilder
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gem "blockbuilder", :path => '../'
@@ -0,0 +1,101 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'block_builder'
4
+
5
+
6
+ # As a convention, you should always document a block builder method and let consumers of
7
+ # this method know how to use it!
8
+ # @example
9
+ # # Here is a code sample on how to use this method, etc
10
+ def create_remote_user(username, password, &block)
11
+ builder = BlockBuilder.build block do
12
+ set :logger, Logger.new
13
+
14
+ # You can force the creation of a callback using the abstract operator. By default,
15
+ # without the abstract modifier, calling an undefined callback will simply do nothing
16
+ abstract :on_user_created
17
+ end
18
+
19
+ # If any callbacks label as 'abstract' aren't defined, then code execution will
20
+ # never reach this line.
21
+
22
+ # Here, you may first want to create the user and create a callback. This callback
23
+ # can then be used to quickly save an entry in the database before other operations
24
+ # are executed. This is useful, for example, if the server crashes during the middle
25
+ # of the request or there is a bug later in the method call. This way, at least you
26
+ # have the user information saved in your local database and you can retrieve the
27
+ # user information later.
28
+ result = api.create_user username, password
29
+ builder.on_user_created(result)
30
+
31
+ # Now we can keep on executing methods and have somewhere else take care of it. Using
32
+ # the set logger variable, we can allow someone to externally change the logger used.
33
+ #
34
+ # Now what if the connection timed out here? Or the server crashed? That's the point
35
+ # of the earlier callback! It allows you to save partial data without interfering
36
+ # with the internal logic of this method!
37
+ start = Time.now
38
+ result2 = api.some_very_time_intensive_call_involving(username)
39
+ builder.logger.info "Executed time intensive call in #{Time.now - start} sec!"
40
+ builder.on_finish_lengthy_task.call(result2)
41
+
42
+ # Another fun use for this: collecting statistics! It might be useful to collect a
43
+ # bunch of statistics for a particular task. However, not every consumer of your
44
+ # method will care for statistics. Just for fun, let's only calculate some
45
+ # statistics about this method call only if they care to calculate stats!
46
+ #
47
+ # We use 'builder.defined?' here because doing
48
+ #
49
+ # builder.generate_statistics.call(some_lengthy_statistic_compilation)
50
+ #
51
+ # will compile statistics, regardless of whether 'generate_statistics' is defined.
52
+ if builder.defined? :generate_statistics
53
+ stats = some_lengthy_statistic_compilation
54
+ builder.generate_statistics.call(stats)
55
+ end
56
+
57
+
58
+ # Now you can add any more callbacks as you see fit! And don't forget, you can still
59
+ # return some data from this method call if you want!
60
+ end
61
+
62
+
63
+ create_remote_user 'user', 'password' do
64
+
65
+ # This easily lets us use our Rails logger
66
+ set :logger, Rails.logger
67
+
68
+ # Remember, our abstract callback was declared, so we must define it here. In this
69
+ # case, assume we have a Ruby on Rails model that deals with saving saved data
70
+ on_user_created do |result|
71
+ u = RemoteUser.new(result)
72
+ u.some_misc_operations
73
+ u.save!
74
+ end
75
+ end
76
+
77
+
78
+ # Assume this is some benchmark suite or testing file
79
+ create_remote_user 'user', 'password' do
80
+ on_user_created do |result|
81
+ # .. Implement here
82
+ end
83
+
84
+ # In this case, we might enjoy statistics
85
+ generate_statistics do |stats|
86
+ puts "Statistics for this operation:"
87
+ puts " * Total execution time: #{stats.exec_time}"
88
+ puts "..."
89
+ end
90
+
91
+ end
92
+
93
+
94
+
95
+ puts "Testing!"
96
+ def test(&block)
97
+ builder = BlockBuilder.build block do
98
+ abstract :my_method
99
+ end
100
+ builder.my_method.call
101
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blockbuilder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christopher Thornton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-23 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Make callbacks easily and build blocks with ease! BlockBuilder is intended
15
+ to be used with more heavy weight method calls that might require intermediate callbacks.
16
+ It's almost like creating classes on the fly!
17
+ email:
18
+ - rmdirbin@gmail.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - .gitignore
24
+ - Gemfile
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - blockbuilder.gemspec
29
+ - lib/block_builder.rb
30
+ - lib/block_builder/attribute.rb
31
+ - lib/block_builder/builder.rb
32
+ - lib/block_builder/version.rb
33
+ - test/Gemfile
34
+ - test/testit.rb
35
+ homepage: https://github.com/cgthornt/BlockBuilder
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 1.8.24
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: Create complex function callbacks with ease!
59
+ test_files:
60
+ - test/Gemfile
61
+ - test/testit.rb
62
+ has_rdoc: