toolchain 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +34 -0
  6. data/LICENSE +22 -0
  7. data/README.md +254 -0
  8. data/Rakefile +1 -0
  9. data/lib/toolchain.rb +2 -0
  10. data/lib/toolchain/attributes.rb +154 -0
  11. data/lib/toolchain/attributes/configuration.rb +41 -0
  12. data/lib/toolchain/attributes/errors.rb +5 -0
  13. data/lib/toolchain/attributes/errors/invalid_hash_transformation.rb +4 -0
  14. data/lib/toolchain/attributes/errors/invalid_mass_assignment.rb +4 -0
  15. data/lib/toolchain/attributes/errors/type_mismatch.rb +4 -0
  16. data/lib/toolchain/attributes/ext/boolean.rb +4 -0
  17. data/lib/toolchain/attributes/helpers.rb +72 -0
  18. data/lib/toolchain/validations.rb +103 -0
  19. data/lib/toolchain/validations/delegation.rb +31 -0
  20. data/lib/toolchain/validations/delegator.rb +51 -0
  21. data/lib/toolchain/validations/helpers.rb +58 -0
  22. data/lib/toolchain/validations/validation_errors.rb +82 -0
  23. data/lib/toolchain/validations/validators.rb +12 -0
  24. data/lib/toolchain/validations/validators/acceptance.rb +25 -0
  25. data/lib/toolchain/validations/validators/base.rb +54 -0
  26. data/lib/toolchain/validations/validators/confirmation.rb +39 -0
  27. data/lib/toolchain/validations/validators/email.rb +26 -0
  28. data/lib/toolchain/validations/validators/exclusion.rb +29 -0
  29. data/lib/toolchain/validations/validators/format.rb +25 -0
  30. data/lib/toolchain/validations/validators/inclusion.rb +29 -0
  31. data/lib/toolchain/validations/validators/length.rb +60 -0
  32. data/lib/toolchain/validations/validators/presence.rb +25 -0
  33. data/lib/toolchain/version.rb +3 -0
  34. data/spec/spec_helper.rb +11 -0
  35. data/spec/toolchain/attributes/attribute_spec.rb +47 -0
  36. data/spec/toolchain/attributes/attributes_spec.rb +193 -0
  37. data/spec/toolchain/attributes/base_helper.rb +37 -0
  38. data/spec/toolchain/attributes/boolean_spec.rb +38 -0
  39. data/spec/toolchain/attributes/configuration_spec.rb +33 -0
  40. data/spec/toolchain/attributes/date_time_spec.rb +21 -0
  41. data/spec/toolchain/attributes/hash_spec.rb +61 -0
  42. data/spec/toolchain/attributes/include_attributes_spec.rb +19 -0
  43. data/spec/toolchain/attributes/initializer_spec.rb +32 -0
  44. data/spec/toolchain/validations/base_helper.rb +2 -0
  45. data/spec/toolchain/validations/custom_validations_spec.rb +26 -0
  46. data/spec/toolchain/validations/delegation_spec.rb +70 -0
  47. data/spec/toolchain/validations/include_validations_spec.rb +20 -0
  48. data/spec/toolchain/validations/inheritence_spec.rb +26 -0
  49. data/spec/toolchain/validations/multiple_validations_spec.rb +19 -0
  50. data/spec/toolchain/validations/nested_validations_spec.rb +68 -0
  51. data/spec/toolchain/validations/validation_errors_spec.rb +9 -0
  52. data/spec/toolchain/validations/validators/acceptance_spec.rb +48 -0
  53. data/spec/toolchain/validations/validators/confirmation_spec.rb +86 -0
  54. data/spec/toolchain/validations/validators/email_spec.rb +41 -0
  55. data/spec/toolchain/validations/validators/exclusion_spec.rb +53 -0
  56. data/spec/toolchain/validations/validators/format_spec.rb +44 -0
  57. data/spec/toolchain/validations/validators/inclusion_spec.rb +50 -0
  58. data/spec/toolchain/validations/validators/length_spec.rb +134 -0
  59. data/spec/toolchain/validations/validators/presence_spec.rb +34 -0
  60. data/toolchain.gemspec +21 -0
  61. metadata +161 -0
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+ require "#{TOOLCHAIN_ROOT}/attributes"
3
+
4
+ require "time"
5
+
6
+ class ModelData
7
+ include Toolchain::Attributes
8
+
9
+ attribute :string, String
10
+ attribute :string_default, String, ""
11
+ attribute :integer, Integer
12
+ attribute :integer_default, Integer, 0
13
+ attribute :float, Float
14
+ attribute :float_default, Float, 0.0
15
+ attribute :array, Array
16
+ attribute :array_default, Array, []
17
+ attribute :hash, Hash
18
+ attribute :hash_default, Hash, {}
19
+ attribute :boolean, Boolean
20
+ attribute :boolean_default, Boolean, false
21
+ attribute :time, Time
22
+ attribute :time_default, Time, -> { Time.now }
23
+ attribute :date, Date
24
+ attribute :date_default, Date, -> { Date.today }
25
+ attribute :date_time, DateTime
26
+ attribute :date_time_default, DateTime, -> { DateTime.now }
27
+ end
28
+
29
+ RSpec.configure do |config|
30
+
31
+ config.before do
32
+ Toolchain::Attributes::Configuration.configure do |config|
33
+ config.hash_transformation = :symbolize_keys
34
+ config.include_nil_in_attributes = false
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Attributes, "Boolean" do
4
+
5
+ let(:model) { ModelData.new }
6
+
7
+ it "should set the boolean to true" do
8
+ expect { model.boolean = true }.
9
+ to change { model.boolean }.from(nil).to(true)
10
+ end
11
+
12
+ it "should accept '1' as true" do
13
+ expect { model.boolean = "1" }.
14
+ to change { model.boolean }.from(nil).to(true)
15
+ end
16
+
17
+ it "should accept 1 as true" do
18
+ expect { model.boolean = 1 }.
19
+ to change { model.boolean }.from(nil).to(true)
20
+ end
21
+
22
+ it "should accept '0' as false" do
23
+ expect { model.boolean = '0' }.
24
+ to change { model.boolean }.from(nil).to(false)
25
+ end
26
+ it "should accept 0 as false" do
27
+ expect { model.boolean = 0 }.
28
+ to change { model.boolean }.from(nil).to(false)
29
+ end
30
+
31
+ it "should raise exception unless type is TrueClass or FalseClass" do
32
+ expect { model.boolean = "Example" }.
33
+ to raise_error(
34
+ Toolchain::Attributes::Errors::TypeMismatch,
35
+ "ModelData#boolean expected [TrueClass, FalseClass] type, " +
36
+ "but received String (Example).")
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Attributes::Configuration do
4
+
5
+ let(:config) { Toolchain::Attributes::Configuration }
6
+
7
+ it "should configure for symbol" do
8
+ config.hash_transformation.should == :symbolize_keys
9
+ end
10
+
11
+ it "should configure for string" do
12
+ expect { config.hash_transformation = :stringify_keys }.
13
+ to change { config.hash_transformation }.
14
+ from(:symbolize_keys).to(:stringify_keys)
15
+ end
16
+
17
+ it "should raise an exception on invalid type" do
18
+ expect { config.configure { |c| c.hash_transformation = :foo } }.
19
+ to raise_error(
20
+ Toolchain::Attributes::Errors::InvalidHashTransformation,
21
+ "valid types: :symbolize_keys, :stringify_keys."
22
+ )
23
+ end
24
+
25
+ it "should configure to omit nil values in attributes" do
26
+ config.include_nil_in_attributes.should == false
27
+ end
28
+
29
+ it "should configure to omit nil values in attributes" do
30
+ expect { config.configure { |c| c.include_nil_in_attributes = true } }.
31
+ to change { config.include_nil_in_attributes }.from(false).to(true)
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Attributes, "Date, Time, DateTime" do
4
+
5
+ let(:model) { ModelData.new }
6
+
7
+ it "should parse the time if string" do
8
+ expect { model.time = Time.new(2000).to_s }.
9
+ to change { model.time }.from(nil).to(Time.new(2000))
10
+ end
11
+
12
+ it "should parse the date if string" do
13
+ expect { model.date = Date.new(2000).to_s }.
14
+ to change { model.date }.from(nil).to(Date.new(2000))
15
+ end
16
+
17
+ it "should parse the date_time if string" do
18
+ expect { model.date_time = DateTime.new(2000).to_s }.
19
+ to change { model.date_time }.from(nil).to(DateTime.new(2000))
20
+ end
21
+ end
@@ -0,0 +1,61 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Attributes, "Hash" do
4
+
5
+ let(:model) { ModelData.new }
6
+
7
+ it "should symbolize keys" do
8
+ model.hash = {
9
+ "foo" => "bar",
10
+ baz: true,
11
+ "deep" => {
12
+ nested: "hash",
13
+ "become" => "symbolized",
14
+ once: {
15
+ "more" => :is_enough
16
+ }
17
+ }
18
+ }
19
+
20
+ model.hash.should == {
21
+ foo: "bar",
22
+ baz: true,
23
+ deep: {
24
+ nested: "hash",
25
+ become: "symbolized",
26
+ once: {
27
+ more: :is_enough
28
+ }
29
+ }
30
+ }
31
+ end
32
+
33
+ it "should stringify keys" do
34
+ Toolchain::Attributes::Configuration.
35
+ hash_transformation = :stringify_keys
36
+
37
+ model.hash = {
38
+ "foo" => "bar",
39
+ baz: true,
40
+ "deep" => {
41
+ nested: "hash",
42
+ "become" => "symbolized",
43
+ once: {
44
+ "more" => :is_enough
45
+ }
46
+ }
47
+ }
48
+
49
+ model.hash.should == {
50
+ "foo" => "bar",
51
+ "baz" => true,
52
+ "deep" => {
53
+ "nested" => "hash",
54
+ "become" => "symbolized",
55
+ "once" => {
56
+ "more" => :is_enough
57
+ }
58
+ }
59
+ }
60
+ end
61
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Attributes, "includeAttributes" do
4
+
5
+ UserExternalAttributes = Proc.new do
6
+ attribute :name, String
7
+ end
8
+
9
+ class UserIncludedAttributes
10
+ include Toolchain::Attributes
11
+ include_attributes UserExternalAttributes
12
+ end
13
+
14
+ it "should include validations" do
15
+ user = UserIncludedAttributes.new
16
+ user.name = "Michael"
17
+ user.name.should == "Michael"
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Attributes, "Initializer" do
4
+
5
+ it "should initialize with attributes" do
6
+ date, time, date_time = Date.today, Time.now, DateTime.now
7
+
8
+ model = ModelData.new(
9
+ array_default: [1, 2, 3],
10
+ boolean_default: true,
11
+ float_default: 1.23,
12
+ hash_default: {foo: :bar},
13
+ integer_default: 123,
14
+ string_default: "value",
15
+ date_default: date,
16
+ time_default: time,
17
+ date_time_default: date_time
18
+ )
19
+
20
+ model.attributes.should == {
21
+ array_default: [1, 2, 3],
22
+ boolean_default: true,
23
+ float_default: 1.23,
24
+ hash_default: {foo: :bar},
25
+ integer_default: 123,
26
+ string_default: "value",
27
+ date_default: date,
28
+ time_default: time,
29
+ date_time_default: date_time
30
+ }
31
+ end
32
+ end
@@ -0,0 +1,2 @@
1
+ require "spec_helper"
2
+ require "#{TOOLCHAIN_ROOT}/validations"
@@ -0,0 +1,26 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Validations, "Custom Validations" do
4
+
5
+ class UserCustomValidation
6
+ include Toolchain::Validations
7
+ attr_accessor :name
8
+ attr_accessor :error_out
9
+ validate :name_validation
10
+
11
+ def name_validation
12
+ errors.add(:name, "custom validation error") if error_out
13
+ end
14
+ end
15
+
16
+ it "should be valid" do
17
+ UserCustomValidation.new.should be_valid
18
+ end
19
+
20
+ it "should be invalid" do
21
+ user = UserCustomValidation.new
22
+ user.error_out = true
23
+ user.should_not be_valid
24
+ user.errors.should == { name: ["custom validation error"] }
25
+ end
26
+ end
@@ -0,0 +1,70 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Validations, "Delegation" do
4
+
5
+ module PersonAttributes
6
+ attr_accessor :name, :email, :terms, :new
7
+
8
+ def keys
9
+ [:name, :email, :terms, :new]
10
+ end
11
+
12
+ def attributes
13
+ { name: name, email: email, terms: terms, new: new }
14
+ end
15
+
16
+ def attributes=(attrs)
17
+ attrs.each do |key, value|
18
+ send("#{key}=", value) if keys.include?(key)
19
+ end
20
+ end
21
+ end
22
+
23
+ class BasePersonValidator
24
+ include PersonAttributes
25
+ include Toolchain::Validations
26
+ include Toolchain::Validations::Delegation
27
+ validates :name, presence: true
28
+ validates :email, email: true
29
+ end
30
+
31
+ class NewPersonValidator < BasePersonValidator
32
+ validates :terms, acceptance: true
33
+ end
34
+
35
+ class ExistingPersonValidator < BasePersonValidator
36
+ end
37
+
38
+ class PersonDelegatorValidation
39
+ include PersonAttributes
40
+ include Toolchain::Validations::Delegator
41
+
42
+ validate_with do |i|
43
+ if i.new
44
+ NewPersonValidator
45
+ else
46
+ ExistingPersonValidator
47
+ end
48
+ end
49
+ end
50
+
51
+ it "should validate name, email, terms" do
52
+ person = PersonDelegatorValidation.new
53
+ person.new = true
54
+ person.should_not be_valid
55
+ person.errors.to_hash.should == {
56
+ name: ["can't be blank"],
57
+ email: ["is invalid"],
58
+ terms: ["must be accepted"]
59
+ }
60
+ end
61
+
62
+ it "should validate name, email" do
63
+ person = PersonDelegatorValidation.new
64
+ person.should_not be_valid
65
+ person.errors.to_hash.should == {
66
+ name: ["can't be blank"],
67
+ email: ["is invalid"]
68
+ }
69
+ end
70
+ end
@@ -0,0 +1,20 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Validations, "IncludeValidations" do
4
+
5
+ UserExternalValidations = Proc.new do
6
+ validates :name, presence: true
7
+ end
8
+
9
+ class UserIncludedValidations
10
+ include Toolchain::Validations
11
+ include_validations UserExternalValidations
12
+ attr_accessor :name
13
+ end
14
+
15
+ it "should include validations" do
16
+ user = UserIncludedValidations.new
17
+ user.should_not be_valid
18
+ user.errors[:name].should == ["can't be blank"]
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Validations, "Inheritence" do
4
+
5
+ class UserInheritingValidations
6
+ include Toolchain::Validations
7
+ attr_reader :name
8
+ validates :name, presence: true
9
+ validate :name_validation
10
+
11
+ private
12
+
13
+ def name_validation
14
+ errors.add(:name, "custom name error")
15
+ end
16
+ end
17
+
18
+ class UserInheritedValidations < UserInheritingValidations
19
+ end
20
+
21
+ it "should be invalid" do
22
+ user = UserInheritedValidations.new
23
+ user.should_not be_valid
24
+ user.errors.should == { name: ["can't be blank", "custom name error"] }
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Validations, "Multiple Validations" do
4
+
5
+ class UserMultipleValidator
6
+ include Toolchain::Validations
7
+ attr_accessor :email
8
+ validates :email,
9
+ presence: { message: "provide an email address" },
10
+ email: { message: "email address isn't valid" }
11
+ end
12
+
13
+ it "should use multiple validators" do
14
+ user = UserMultipleValidator.new
15
+ user.should_not be_valid
16
+ user.errors[:email].should ==
17
+ ["provide an email address", "email address isn't valid"]
18
+ end
19
+ end
@@ -0,0 +1,68 @@
1
+ require_relative "base_helper"
2
+
3
+ describe Toolchain::Validations, "Nested Validation" do
4
+
5
+ class UserNestedValidation
6
+ include Toolchain::Validations
7
+ attr_accessor :config
8
+ validates :config, :name, presence: true
9
+ validates :config, :email, email: true
10
+ end
11
+
12
+ let(:user) { UserNestedValidation.new }
13
+
14
+ it "should be valid" do
15
+ user.config = {
16
+ name: "Example",
17
+ email: "example@machinery.io"
18
+ }
19
+ user.should be_valid
20
+ end
21
+
22
+ it "is invalid if nil" do
23
+ user.should_not be_valid
24
+ user.errors[:config][:name].should == ["can't be blank"]
25
+ user.errors[:config][:email].should == ["is invalid"]
26
+ end
27
+
28
+ class UserDeeplyNestedValidation
29
+ include Toolchain::Validations
30
+ attr_accessor :config
31
+ validates :config, :deep, :name, presence: true
32
+ validates :config, :deep, :email, email: true
33
+ end
34
+
35
+ it "should be valid" do
36
+ user = UserDeeplyNestedValidation.new
37
+ user.config = {
38
+ deep: {
39
+ name: "Example",
40
+ email: "example@machinery.io"
41
+ }
42
+ }
43
+ user.should be_valid
44
+ end
45
+
46
+ it "is invalid if nil" do
47
+ user = UserDeeplyNestedValidation.new
48
+ user.config = {}
49
+ user.should_not be_valid
50
+ user.errors[:config][:deep][:name].should == ["can't be blank"]
51
+ end
52
+
53
+ class UserCustomNestedValidation
54
+ include Toolchain::Validations
55
+ attr_accessor :config
56
+ validate :name_validation
57
+
58
+ def name_validation
59
+ errors.add(:config, :name, "custom validation error")
60
+ end
61
+ end
62
+
63
+ it "should be invalid" do
64
+ user = UserCustomNestedValidation.new
65
+ user.should_not be_valid
66
+ user.errors.should == { config: { name: ["custom validation error"] } }
67
+ end
68
+ end