subtle 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rvmrc +55 -0
- data/.travis.yml +12 -0
- data/Gemfile +14 -0
- data/Guardfile +12 -0
- data/LICENSE +22 -0
- data/README.md +123 -0
- data/Rakefile +10 -0
- data/lib/subtle.rb +6 -0
- data/lib/subtle/array_to_object.rb +32 -0
- data/lib/subtle/cover.rb +29 -0
- data/lib/subtle/lambda_to_object.rb +5 -0
- data/lib/subtle/lazy_cover.rb +27 -0
- data/lib/subtle/param_constructor.rb +10 -0
- data/lib/subtle/safety_proc.rb +7 -0
- data/lib/subtle/version.rb +3 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/subtle/array_to_object_spec.rb +99 -0
- data/spec/subtle/cover_spec.rb +94 -0
- data/spec/subtle/lambda_to_object_spec.rb +39 -0
- data/spec/subtle/lazy_cover_spec.rb +66 -0
- data/spec/subtle/param_constructor_spec.rb +19 -0
- data/spec/subtle/safety_proc_spec.rb +35 -0
- data/subtle.gemspec +22 -0
- metadata +130 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
|
7
|
+
environment_id="ruby-1.9.3-p0@subtle"
|
8
|
+
|
9
|
+
#
|
10
|
+
# Uncomment following line if you want options to be set only for given project.
|
11
|
+
#
|
12
|
+
# PROJECT_JRUBY_OPTS=( --1.9 )
|
13
|
+
|
14
|
+
#
|
15
|
+
# First we attempt to load the desired environment directly from the environment
|
16
|
+
# file. This is very fast and efficient compared to running through the entire
|
17
|
+
# CLI and selector. If you want feedback on which environment was used then
|
18
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
19
|
+
#
|
20
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
|
21
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
22
|
+
then
|
23
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
24
|
+
|
25
|
+
if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
|
26
|
+
then
|
27
|
+
. "${rvm_path:-$HOME/.rvm}/hooks/after_use"
|
28
|
+
fi
|
29
|
+
else
|
30
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
31
|
+
if ! rvm --create "$environment_id"
|
32
|
+
then
|
33
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
34
|
+
return 1
|
35
|
+
fi
|
36
|
+
fi
|
37
|
+
|
38
|
+
#
|
39
|
+
# If you use an RVM gemset file to install a list of gems (*.gems), you can have
|
40
|
+
# it be automatically loaded. Uncomment the following and adjust the filename if
|
41
|
+
# necessary.
|
42
|
+
#
|
43
|
+
# filename=".gems"
|
44
|
+
# if [[ -s "$filename" ]]
|
45
|
+
# then
|
46
|
+
# rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
|
47
|
+
# fi
|
48
|
+
|
49
|
+
# If you use bundler, this might be useful to you:
|
50
|
+
# if command -v bundle && [[ -s Gemfile ]]
|
51
|
+
# then
|
52
|
+
# bundle install
|
53
|
+
# fi
|
54
|
+
|
55
|
+
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
guard 'minitest' do
|
2
|
+
watch(%r|^test/test_(.*)\.rb|)
|
3
|
+
watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
4
|
+
watch(%r|^test/test_helper\.rb|) { "test" }
|
5
|
+
watch(%r|^lib/(.*)\.rb|) { |m| "test/test_#{m[1]}.rb" }
|
6
|
+
|
7
|
+
watch(%r{^spec/.+_spec\.rb$})
|
8
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
9
|
+
watch(%r{^lib/subtle/(.+)\.rb$}) { |m| "spec/subtle/#{m[1]}_spec.rb" }
|
10
|
+
watch(%r{^spec/models/.+\.rb$}) { ["spec/models", "spec/acceptance"] }
|
11
|
+
watch('spec/spec_helper.rb') { "spec" }
|
12
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Darren Cauthon
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# Subtle
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
I don't like "grab-bag" method libraries any more than anybody, but also like everybody: I'll make an exception for **MINE**. :)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'subtle'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
gem install subtle
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Arrays to Objects
|
23
|
+
|
24
|
+
This feature exists to make the creation of sets of objects with data easily.
|
25
|
+
|
26
|
+
* Create an array of symbols of symbols that match the properties on the objects you want, and
|
27
|
+
* Pass a block that returns an array of arrays with matching data.
|
28
|
+
|
29
|
+
````ruby
|
30
|
+
records = [:first_name, :last_name].to_objects {
|
31
|
+
[
|
32
|
+
["John", "Galt"],
|
33
|
+
["Howard", "Roark"],
|
34
|
+
["Dagny", "Taggart"]
|
35
|
+
]}
|
36
|
+
|
37
|
+
records[0].first_name # "John"
|
38
|
+
records[0].last_name # "Galt"
|
39
|
+
|
40
|
+
records[1].first_name # "Howard"
|
41
|
+
records[1].last_name # "Roark"
|
42
|
+
|
43
|
+
records[2].first_name # "Dagny"
|
44
|
+
records[2].last_name # "Taggart"
|
45
|
+
````
|
46
|
+
|
47
|
+
### Safety Proc
|
48
|
+
|
49
|
+
This feature was written because I hate wrapping code in begin/rescue/end blocks. If I have a line of code and I don't particularly care if it fails, I have to wrap it in three more lines of care to stop exceptions.
|
50
|
+
|
51
|
+
To me, this is most useful in import work or one-off processes where I might want to check to run a small block of code regardless of whether it fails. This could lead to less code not just because of the lost begin/rescue/end lines, but because code can be written without concern of whether it will fail (nil checks, etc.).
|
52
|
+
|
53
|
+
````ruby
|
54
|
+
person.name = document.at_xpath('./h1').text
|
55
|
+
|
56
|
+
# if this call fails then we will move on
|
57
|
+
-> { person.bio = document.xpath('./div[@class="bio_info"]//span').text }.call_safely
|
58
|
+
|
59
|
+
# if this call fails then the second block will be called
|
60
|
+
-> { person.special = document.xpath('./div[@class="active"]//a')[1].text == "special" }.call_safely { person.special = false }
|
61
|
+
````
|
62
|
+
|
63
|
+
### Param Constructor
|
64
|
+
|
65
|
+
One thing I liked about C# was the ability to instantiate my objects like this:
|
66
|
+
|
67
|
+
````c#
|
68
|
+
var person = new Person() { FirstName = "John", LastName = "Galt" };
|
69
|
+
````
|
70
|
+
|
71
|
+
This syntax is not built into Ruby syntax today, but it does exist in Rails models. So I took that idea from Rails and wrote an implementation that works like this:
|
72
|
+
|
73
|
+
````ruby
|
74
|
+
class Person
|
75
|
+
params_constructor
|
76
|
+
attr_accessor :first_name, :last_name
|
77
|
+
end
|
78
|
+
|
79
|
+
person = Person.new(first_name: "John", last_name: "Galt")
|
80
|
+
````
|
81
|
+
|
82
|
+
### Proc to Object
|
83
|
+
|
84
|
+
I was inspired to write this feature while dealing with some bad Rails code. A programmer wrote a before_filter on ApplicationController that made a big, expensive web service call to pass the users current weather information to the view. This weather information was shown in various places on the site, but there were many pages on the site where the data was not being used at all.
|
85
|
+
|
86
|
+
A thought came to me... *would it be possible to create an object that does the work to instantiate itself, but only when it is referenced?*
|
87
|
+
|
88
|
+
Well, this doesn't quite do that, but it's close. It lets you turn this:
|
89
|
+
|
90
|
+
````ruby
|
91
|
+
before_filter do
|
92
|
+
service = BigExpensiveWeatherService.new
|
93
|
+
# we just paid the price right now
|
94
|
+
@weather_results = service.an_expensive_web_call
|
95
|
+
end
|
96
|
+
````
|
97
|
+
|
98
|
+
into this:
|
99
|
+
|
100
|
+
````ruby
|
101
|
+
before_filter do
|
102
|
+
# we haven't paid the price for this call
|
103
|
+
@weather_results = -> do
|
104
|
+
service = BigExpensiveWeatherService.new
|
105
|
+
service.an_expensive_web_call
|
106
|
+
end.to_object
|
107
|
+
end
|
108
|
+
````
|
109
|
+
|
110
|
+
With both, you could do this:
|
111
|
+
|
112
|
+
````haml
|
113
|
+
%span
|
114
|
+
= @weather_results.temperature
|
115
|
+
````
|
116
|
+
|
117
|
+
But with the latter, the call to execute the big web service won't be made until .temperature is called. Future calls to methods on @weather_results will use the same object passed from the Proc.
|
118
|
+
|
119
|
+
I know, I know... there are lots of reasons **NOT** to do this, and I'm not saying to do this all the time, but it's neat to know that it is possible.
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
|
data/Rakefile
ADDED
data/lib/subtle.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
class Array
|
2
|
+
|
3
|
+
def to_objects(&blk)
|
4
|
+
records = blk.call
|
5
|
+
return [] if records.empty?
|
6
|
+
records.map { |record| create_object_for_this_record(record) }
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def create_object_for_this_record(record)
|
12
|
+
result = Object.new
|
13
|
+
self.each_with_index do |property_name, index|
|
14
|
+
value = get_the_value(record, index)
|
15
|
+
add_reader_for(result, property_name, value)
|
16
|
+
end
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_the_value(value, index)
|
21
|
+
return value unless value.kind_of?(Array)
|
22
|
+
value[index]
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_reader_for(result, property_name, this_value)
|
26
|
+
result.instance_variable_set("@#{property_name}", this_value)
|
27
|
+
result.instance_eval("
|
28
|
+
class << self
|
29
|
+
attr_accessor :#{property_name}
|
30
|
+
end")
|
31
|
+
end
|
32
|
+
end
|
data/lib/subtle/cover.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'blankslate'
|
2
|
+
|
3
|
+
module Subtle
|
4
|
+
module CoverMethods
|
5
|
+
def initialize(subject)
|
6
|
+
@subject = subject
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(meth, *args, &blk)
|
10
|
+
@subject.send(meth, *args, &blk)
|
11
|
+
rescue
|
12
|
+
Subtle::Cover.new Object.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def the_original_subject
|
16
|
+
@subject
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module StrictCoverMethods
|
21
|
+
def method_missing(meth, *args, &blk)
|
22
|
+
@subject.send(meth, *args, &blk)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Cover < ::BlankSlate
|
27
|
+
include Subtle::CoverMethods
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Subtle
|
2
|
+
class LazyCover < Cover
|
3
|
+
include Subtle::StrictCoverMethods
|
4
|
+
|
5
|
+
def initialize(block)
|
6
|
+
@block = block
|
7
|
+
end
|
8
|
+
|
9
|
+
alias :the_original_subject_from_the_base_class :the_original_subject
|
10
|
+
def the_original_subject
|
11
|
+
setup_the_subject
|
12
|
+
the_original_subject_from_the_base_class
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
alias :old_method_missing :method_missing
|
18
|
+
def method_missing(meth, *args, &blk)
|
19
|
+
setup_the_subject
|
20
|
+
old_method_missing meth, *args, &blk
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup_the_subject
|
24
|
+
@subject = @block.call if @subject.nil?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "arrays to objects" do
|
4
|
+
|
5
|
+
[:name, :street].each do |property|
|
6
|
+
describe "with an array with one symbol named #{property.to_s}" do
|
7
|
+
describe "and no data" do
|
8
|
+
before do
|
9
|
+
@values = [property].to_objects { [] }
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return an empty array" do
|
13
|
+
@values.must_be_empty
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
["Dangy", "Taggart"].each do |name|
|
18
|
+
describe "and one record with #{name}" do
|
19
|
+
before do
|
20
|
+
@values = [property].to_objects { [name] }
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return one record" do
|
24
|
+
@values.count.must_equal 1
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should have a property with the name of Dangy" do
|
28
|
+
@values[0].send(property).must_equal name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "and two records with John and Howard" do
|
34
|
+
before do
|
35
|
+
@values = [property].to_objects {["John", "Howard"]}
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return two records" do
|
39
|
+
@values.count.must_equal 2
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should set the first to John" do
|
43
|
+
@values[0].send(property).must_equal "John"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should set the second to Howard" do
|
47
|
+
@values[1].send(property).must_equal "Howard"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "with an array with two symbols named first_name and last_name" do
|
53
|
+
describe "with 'Ellis' and 'Wyatt'" do
|
54
|
+
before do
|
55
|
+
@values = [:first_name, :last_name].to_objects { [['Ellis', 'Wyatt']] }
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return one record" do
|
59
|
+
@values.count.must_equal 1
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should set the first name to Ellis" do
|
63
|
+
@values[0].first_name.must_equal 'Ellis'
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should set the last name to Wyatt" do
|
67
|
+
@values[0].last_name.must_equal 'Wyatt'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "with 'Ellis' and 'Wyatt', then 'Dagny' and 'Taggart'" do
|
72
|
+
before do
|
73
|
+
@values = [:first_name, :last_name].to_objects { [['Ellis', 'Wyatt'], ['Dagny', 'Taggart']] }
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should return one record" do
|
77
|
+
@values.count.must_equal 2
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should set the first name to Ellis on the first record" do
|
81
|
+
@values[0].first_name.must_equal 'Ellis'
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should set the last name to Wyatt on the first record" do
|
85
|
+
@values[0].last_name.must_equal 'Wyatt'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should set the first name to Dagny on the second record" do
|
89
|
+
@values[1].first_name.must_equal 'Dagny'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should set the last name to Wyatt on the second record" do
|
93
|
+
@values[1].last_name.must_equal 'Taggart'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Subtle::Cover do
|
4
|
+
before do
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "when given an Object" do
|
8
|
+
before do
|
9
|
+
@view_model = Subtle::Cover.new Object.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not throw an exception on methods that do not exist" do
|
13
|
+
@view_model.not_this
|
14
|
+
@view_model.or_this_either
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return an object if the method does not exist" do
|
18
|
+
@view_model.should_be_a_view_model.wont_be_nil
|
19
|
+
@view_model.another_test.wont_be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return new view models each time on methods that do not exist" do
|
23
|
+
first = @view_model.one_call
|
24
|
+
second = @view_model.second_call
|
25
|
+
|
26
|
+
first.object_id.wont_equal @view_model.object_id
|
27
|
+
second.object_id.wont_equal @view_model.object_id
|
28
|
+
first.object_id.wont_equal second.object_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "when given a Person object" do
|
33
|
+
class Person
|
34
|
+
attr_accessor :first_name, :last_name
|
35
|
+
end
|
36
|
+
|
37
|
+
before do
|
38
|
+
@person = Person.new
|
39
|
+
@view_model = Subtle::Cover.new @person
|
40
|
+
end
|
41
|
+
|
42
|
+
[["John", "Galt"], ["Howard", "Roark"]].each do |name|
|
43
|
+
describe "with name of #{name[0]} #{name[1]}" do
|
44
|
+
before do
|
45
|
+
@person.first_name = name[0]
|
46
|
+
@person.last_name = name[1]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return John as the first name" do
|
50
|
+
@view_model.first_name.must_equal name[0]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return Galt as the last name" do
|
54
|
+
@view_model.last_name.must_equal name[1]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "when given an object that uses a block" do
|
61
|
+
class Thing
|
62
|
+
class << self
|
63
|
+
attr_accessor :test_value
|
64
|
+
end
|
65
|
+
def test(&blk)
|
66
|
+
blk.call
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
before do
|
71
|
+
@thing = Thing.new
|
72
|
+
@view_model = Subtle::Cover.new @thing
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should call the block" do
|
76
|
+
@view_model.test do
|
77
|
+
Thing.test_value = true
|
78
|
+
end
|
79
|
+
Thing.test_value.must_equal true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
[1, "abc", Object.new].each do |subject|
|
84
|
+
describe "#the_original_subject" do
|
85
|
+
before do
|
86
|
+
@cover = Subtle::Cover.new subject
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return the original" do
|
90
|
+
@cover.the_original_subject.must_equal subject
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "lambda to object" do
|
4
|
+
|
5
|
+
describe "to_object on a lambda that returns nil" do
|
6
|
+
before do
|
7
|
+
@value = -> { nil }.to_object
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should remember the original value is nil" do
|
11
|
+
@value.the_original_subject.must_equal nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "to_object on a lambda that returns an integer" do
|
16
|
+
before do
|
17
|
+
@value = -> { 1 }.to_object
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should remember the original value is 1" do
|
21
|
+
@value.the_original_subject.must_equal 1
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be able to use the value as an integer" do
|
25
|
+
(@value + 1).must_equal 2
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "to_object on a lambda, but never accessing the variable" do
|
30
|
+
before do
|
31
|
+
@was_called = false
|
32
|
+
@value = -> { raise 'it was called' }.to_object
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should not be called" do
|
36
|
+
#will throw an exception if it was called above
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'mocha'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
3
|
+
|
4
|
+
class LazyCoverTest
|
5
|
+
class << self
|
6
|
+
attr_accessor :was_called
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Subtle::LazyCover do
|
11
|
+
|
12
|
+
it "should not call the block passed to it" do
|
13
|
+
lazy = Subtle::LazyCover.new(-> { raise 'was called' })
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should call the block passed to it when things are references" do
|
17
|
+
LazyCoverTest.was_called = false
|
18
|
+
lazy = Subtle::LazyCover.new(-> { LazyCoverTest.was_called = true })
|
19
|
+
LazyCoverTest.was_called.must_equal false
|
20
|
+
lazy.to_s
|
21
|
+
LazyCoverTest.was_called.must_equal true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should call the block passed to it only once" do
|
25
|
+
LazyCoverTest.was_called = 0
|
26
|
+
lazy = Subtle::LazyCover.new(-> { LazyCoverTest.was_called = LazyCoverTest.was_called + 1 })
|
27
|
+
LazyCoverTest.was_called.must_equal 0
|
28
|
+
lazy.to_s
|
29
|
+
LazyCoverTest.was_called.must_equal 1
|
30
|
+
lazy.to_s
|
31
|
+
LazyCoverTest.was_called.must_equal 1
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return the same exceptions passed from its base class methods" do
|
35
|
+
|
36
|
+
object = Object.new
|
37
|
+
object.stubs(:defined_method).returns("yes")
|
38
|
+
|
39
|
+
lazy = Subtle::LazyCover.new(-> { object })
|
40
|
+
lazy.defined_method.must_equal "yes"
|
41
|
+
was_called = false
|
42
|
+
begin
|
43
|
+
lazy.not_a_defined_method
|
44
|
+
rescue NoMethodError
|
45
|
+
was_called = true
|
46
|
+
end
|
47
|
+
was_called.must_equal true
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "the original subject" do
|
51
|
+
it "should call the block passed to it" do
|
52
|
+
Subtle::LazyCover.new(-> { 0 }).the_original_subject.must_equal 0
|
53
|
+
Subtle::LazyCover.new(-> { "x" }).the_original_subject.must_equal "x"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should only call the block passed to it once" do
|
57
|
+
LazyCoverTest.was_called = 0
|
58
|
+
lazy = Subtle::LazyCover.new(-> { LazyCoverTest.was_called += 1 })
|
59
|
+
lazy.the_original_subject
|
60
|
+
LazyCoverTest.was_called.must_equal 1
|
61
|
+
lazy.the_original_subject
|
62
|
+
LazyCoverTest.was_called.must_equal 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
class ParamConstructorTest
|
4
|
+
param_constructor
|
5
|
+
attr_accessor :first_name, :last_name
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "param_constructor" do
|
9
|
+
it "should let the object be instantiated with a hash" do
|
10
|
+
test = ParamConstructorTest.new(first_name: "John", last_name: "Galt")
|
11
|
+
test.first_name.must_equal "John"
|
12
|
+
test.last_name.must_equal "Galt"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should allow the object to be instantiated with no params" do
|
16
|
+
test = ParamConstructorTest.new
|
17
|
+
# should not throw an error
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
module SafetyProc
|
4
|
+
class << self
|
5
|
+
attr_accessor :called
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "Safety Proc" do
|
10
|
+
describe "a proc that will throw an exception" do
|
11
|
+
before do
|
12
|
+
SafetyProc.called = false
|
13
|
+
@proc = -> do
|
14
|
+
SafetyProc.called = true
|
15
|
+
raise 'k'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should trap the exception yet still call the method" do
|
20
|
+
@proc.call_safely
|
21
|
+
SafetyProc.called.must_equal true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "a proc with a block to call on error" do
|
26
|
+
before do
|
27
|
+
SafetyProc.called = false
|
28
|
+
end
|
29
|
+
|
30
|
+
it "call the block" do
|
31
|
+
@proc = -> { raise 'k' }.call_safely { SafetyProc.called = true }
|
32
|
+
SafetyProc.called.must_equal true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/subtle.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/subtle/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Darren Cauthon"]
|
6
|
+
gem.email = ["darren@cauthon.com"]
|
7
|
+
gem.description = %q{A few small updates to make even more concise Ruby.}
|
8
|
+
gem.summary = %q{A few small updates to make even more concise Ruby.}
|
9
|
+
gem.homepage = "http://www.github.com/darrencauthon/subtle"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "subtle"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Subtle::VERSION
|
17
|
+
|
18
|
+
# specify any dependencies here; for example:
|
19
|
+
gem.add_runtime_dependency 'blankslate'
|
20
|
+
gem.add_development_dependency 'mocha'
|
21
|
+
gem.add_development_dependency 'rake'
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: subtle
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.4
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Darren Cauthon
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: blankslate
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mocha
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: A few small updates to make even more concise Ruby.
|
63
|
+
email:
|
64
|
+
- darren@cauthon.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- .rvmrc
|
71
|
+
- .travis.yml
|
72
|
+
- Gemfile
|
73
|
+
- Guardfile
|
74
|
+
- LICENSE
|
75
|
+
- README.md
|
76
|
+
- Rakefile
|
77
|
+
- lib/subtle.rb
|
78
|
+
- lib/subtle/array_to_object.rb
|
79
|
+
- lib/subtle/cover.rb
|
80
|
+
- lib/subtle/lambda_to_object.rb
|
81
|
+
- lib/subtle/lazy_cover.rb
|
82
|
+
- lib/subtle/param_constructor.rb
|
83
|
+
- lib/subtle/safety_proc.rb
|
84
|
+
- lib/subtle/version.rb
|
85
|
+
- spec/spec_helper.rb
|
86
|
+
- spec/subtle/array_to_object_spec.rb
|
87
|
+
- spec/subtle/cover_spec.rb
|
88
|
+
- spec/subtle/lambda_to_object_spec.rb
|
89
|
+
- spec/subtle/lazy_cover_spec.rb
|
90
|
+
- spec/subtle/param_constructor_spec.rb
|
91
|
+
- spec/subtle/safety_proc_spec.rb
|
92
|
+
- subtle.gemspec
|
93
|
+
homepage: http://www.github.com/darrencauthon/subtle
|
94
|
+
licenses: []
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
hash: 867729927420554654
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
hash: 867729927420554654
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.8.24
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: A few small updates to make even more concise Ruby.
|
123
|
+
test_files:
|
124
|
+
- spec/spec_helper.rb
|
125
|
+
- spec/subtle/array_to_object_spec.rb
|
126
|
+
- spec/subtle/cover_spec.rb
|
127
|
+
- spec/subtle/lambda_to_object_spec.rb
|
128
|
+
- spec/subtle/lazy_cover_spec.rb
|
129
|
+
- spec/subtle/param_constructor_spec.rb
|
130
|
+
- spec/subtle/safety_proc_spec.rb
|