smart_core 0.0.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fafaa0194f7b04c748fbfcd665bfbd0d13b7ce822cddc66694e0b8c417bd2c63
4
- data.tar.gz: ba54750965b25c714c87696ad0c5dbe1cb1d1ebfaf2b93631afcbd79687bca18
3
+ metadata.gz: 386622ee9696b78aaf7e86cfcc5eaa6e11591bab6607a355d85b9fbfa90d513a
4
+ data.tar.gz: 9a905a991a12f36dc775076afa4f9069c099c33351e1d19911be4087789f8858
5
5
  SHA512:
6
- metadata.gz: c1a4572366fc8cc0047cd30b9513002c32936f3bfc565887cd245fb51ca68b01e89aa85fae9bd26bb725fd9b51b787db798e0955a399b5ebd1b6fea016d36c5f
7
- data.tar.gz: d5d32d60ea55b12590a39b178711cf8c2f354e0565e9a6e863544776764e7951213e5886fca9fc21153e65020c5ca7f94a91ed50881990f477c436050417b295
6
+ metadata.gz: 1d7d6a650e4b4656e932883da4669b3eb2659a1b32e68d70db5eaf48f6d37edd8470ac2400ab3822df60a475ebd669c761899ed08d4613282ef8738be22eda5f
7
+ data.tar.gz: 648eb736394268e455e478431df012b7658cb434e8fc5ab506fe16f3f81a005fd7fa3fa1c1d1777a0395cd8899490ffc1a47c1f3e05314cf5a1aca078c4662b1
data/.gitignore CHANGED
@@ -1,11 +1,13 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
+ /Gemfile.lock
3
4
  /_yardoc/
4
5
  /coverage/
5
6
  /doc/
6
7
  /pkg/
7
8
  /spec/reports/
8
9
  /tmp/
9
-
10
- # rspec failure tracking
11
10
  .rspec_status
11
+ .ruby-version
12
+ gemfiles/*.lock
13
+ *.gem
data/.rspec CHANGED
@@ -1,3 +1,3 @@
1
- --format documentation
2
1
  --color
2
+ --format=progress
3
3
  --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,15 @@
1
+ inherit_gem:
2
+ armitage-rubocop:
3
+ - lib/rubocop.general.yml
4
+ - lib/rubocop.rspec.yml
5
+
6
+ AllCops:
7
+ UseCache: true
8
+ TargetRubyVersion: 2.6.0
9
+ Include:
10
+ - bin/console
11
+ - lib/**/*.rb
12
+ - spec/**/*.rb
13
+ - Gemfile
14
+ - Rakefile
15
+ - smart_core.gemspec
data/.travis.yml CHANGED
@@ -1,5 +1,16 @@
1
- sudo: false
2
1
  language: ruby
2
+ sudo: false
3
+ before_install: gem install bundler
4
+ cache: bundler
3
5
  rvm:
4
- - 2.5.1
5
- before_install: gem install bundler -v 1.16.2
6
+ - 2.3.8
7
+ - 2.4.5
8
+ - 2.5.3
9
+ - 2.6.0
10
+ - ruby-head
11
+ - jruby-head
12
+ script:
13
+ - bundle exec rspec
14
+ notifications:
15
+ slack:
16
+ secure: CUM3xnSJvn/y6Z+epQrO5uSk1EUuP4S+ugcS+48nlWD6eEzhYZwcBAK9tvD6kYGOPzoWyKSbW6Is1kzSunNr6f8ICc8HtrVHzoHE8UCzwZ7FpjgD0JgMgC4+xLLBO6lV0Hc7dNh4KLAhy4Zvbr09DPjcG8UW9/QgUdn+wBf9LHMgA+2SpBPEzMwz6pMbWW/+kkoCz2KU877kEfuFFGfvoOEFWTxUUhuCYi5a1SmTjm9A7CFTEWNkX8yHJwdgVgONjvm9lnhTeV0DvMeEOhk8Kx/hzQ2OGIicXZXWyZFkzNzpQm5xE12Hs4rY7mGPbo3bsYRDH9Ys6yf4fpNf0oZ/1agzl/gnDzacA2wL2b+eq1uZblE/fdwrNp+GIH0R+Ec2rsVKM7kh71sA6Xp1pzFb2DnHj5O4QFaPMsLyIZqD0BkVt4pNfwi4ZUB56sWkYeRQGm8x2Beh02IyFfxI65lY5kZrW6GjXx5eFeOHcmzQzeADWLSBf4iw01G/A/QqwZzlZ3fm0fRJi8BKDh3Jj0IrGndE9Bl+E7uOClKKngTPxOVLOLo/vY5mfI8M94f99kr0J45XkoAdc0SQfi3hDa/tHaakI5GJsmByjgOQwV7OU/2wzJoXAg/RPYjsYcwnTgzm9bLXPhQzfM8rUS5vLTOWIW+N0QKmWml7eWVtiedcwXA=
data/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## [0.1.0] - 2019-01-05
5
+ ### Added
6
+ - Validation object abstraction;
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in smart_core.gemspec
6
8
  gemspec
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Rustam Ibragimov
3
+ Copyright (c) 2018-2019 Rustam Ibragimov
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,43 +1,12 @@
1
- # SmartCore
1
+ # SmartCore · [![Build Status](https://travis-ci.org/0exp/smart_core.svg?branch=master)](https://travis-ci.org/0exp/smart_core) [![Coverage Status](https://coveralls.io/repos/github/0exp/smart_core/badge.svg?branch=master)](https://coveralls.io/github/0exp/smart_core?branch=master)
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/smart_core`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ In active development.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ #### Completed abstractions:
6
6
 
7
- ## Installation
8
-
9
- Add this line to your application's Gemfile:
10
-
11
- ```ruby
12
- gem 'smart_core'
13
- ```
14
-
15
- And then execute:
16
-
17
- $ bundle
18
-
19
- Or install it yourself as:
20
-
21
- $ gem install smart_core
22
-
23
- ## Usage
24
-
25
- TODO: Write usage instructions here
26
-
27
- ## Development
28
-
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
-
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
-
33
- ## Contributing
34
-
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/smart_core. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
-
37
- ## License
38
-
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
40
-
41
- ## Code of Conduct
42
-
43
- Everyone interacting in the SmartCore project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/smart_core/blob/master/CODE_OF_CONDUCT.md).
7
+ - **Validation object** (`SmartCore::Validator`)
8
+ - support for nested validations;
9
+ - inheritance works as expected `:)`;
10
+ - command-style DSL;
11
+ - thread-safe;
12
+ - no dependencies;
data/Rakefile CHANGED
@@ -1,6 +1,18 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
3
2
 
4
- RSpec::Core::RakeTask.new(:spec)
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop'
6
+ require 'rubocop-rspec'
7
+ require 'rubocop/rake_task'
5
8
 
6
- task :default => :spec
9
+ RuboCop::RakeTask.new(:rubocop) do |t|
10
+ config_path = File.expand_path(File.join('.rubocop.yml'), __dir__)
11
+
12
+ t.options = ['--config', config_path]
13
+ t.requires << 'rubocop-rspec'
14
+ end
15
+
16
+ RSpec::Core::RakeTask.new(:rspec)
17
+
18
+ task default: :rspec
data/bin/console CHANGED
@@ -1,14 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "smart_core"
4
+ require 'bundler/setup'
5
+ require 'smart_core'
5
6
 
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
7
+ require 'pry'
8
+ Pry.start
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Validator::Attribute
6
+ # @return [Symbol]
7
+ #
8
+ # @api private
9
+ # @since 0.1.0
10
+ attr_reader :name
11
+
12
+ # @param name [String, Symbol]
13
+ # @param default_value [Proc, Object]
14
+ # @return [void]
15
+ #
16
+ # @api private
17
+ # @since 0.1.0
18
+ def initialize(name, default_value = nil)
19
+ unless name.is_a?(Symbol) || name.is_a?(String)
20
+ raise(
21
+ SmartCore::Validator::IncorrectAttributeNameError,
22
+ 'Attribute name should be a symbol or a string'
23
+ )
24
+ end
25
+
26
+ @name = name.to_sym
27
+ @default_value = default_value
28
+ end
29
+
30
+ # @return [Any]
31
+ #
32
+ # @api private
33
+ # @since 0.1.0
34
+ def default_value
35
+ @default_value.is_a?(Proc) ? @default_value.call : @default_value
36
+ end
37
+
38
+ # @return [SmartCore::Validator::Attribute]
39
+ #
40
+ # @api private
41
+ # @since 0.1.0
42
+ def dup
43
+ self.class.new(name, @default_value)
44
+ end
45
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Validator::AttributeSet
6
+ # @since 0.1.0
7
+ include Enumerable
8
+
9
+ # @return [Hash<Symbol, SmartCore::Validator::Attribute>]
10
+ #
11
+ # @api private
12
+ # @since 0.1.0
13
+ attr_reader :attributes
14
+
15
+ # @return [void]
16
+ #
17
+ # @api private
18
+ # @since 0.1.0
19
+ def initialize
20
+ @attributes = {}
21
+ @access_lock = Mutex.new
22
+ end
23
+
24
+ # @param attribute [Symbiont::Validator::Attribute]
25
+ # @return [void]
26
+ #
27
+ # @api private
28
+ # @since 0.1.0
29
+ def add_attribute(attribute)
30
+ thread_safe { attributes[attribute.name] = attribute }
31
+ end
32
+ alias_method :<<, :add_attribute
33
+
34
+ # @param attribute_set [SmartCore::Validator::AttributeSet]
35
+ # @return [void]
36
+ #
37
+ # @api private
38
+ # @sinec 0.1.0
39
+ def concat(attribute_set)
40
+ thread_safe { attributes.merge!(attribute_set.dup.attributes) }
41
+ end
42
+
43
+ # @return [SmartCore::Validator::AttributeSet]
44
+ #
45
+ # @api private
46
+ # @since 0.1.0
47
+ def dup
48
+ thread_safe do
49
+ self.class.new.tap do |duplicate|
50
+ attributes.each_value do |attribute|
51
+ duplicate.add_attribute(attribute.dup)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ # @return [Enumerable]
58
+ #
59
+ # @api private
60
+ # @since 0.1.0
61
+ def each(&block)
62
+ thread_safe { block_given? ? attributes.each_value(&block) : attributes.each_value }
63
+ end
64
+
65
+ private
66
+
67
+ # @param block [Proc]
68
+ # @return [Any]
69
+ #
70
+ # @api private
71
+ # @since 0.1.0
72
+ def thread_safe(&block)
73
+ @access_lock.synchronize(&block)
74
+ end
75
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Validator::CommandSet
6
+ # @since 0.1.0
7
+ include Enumerable
8
+
9
+ # @return [Array<SmartCore::Validator::Commands::Base>]
10
+ #
11
+ # @api private
12
+ # @since 0.1.0
13
+ attr_reader :commands
14
+
15
+ # @api private
16
+ # @since 0.1.0
17
+ def initialize
18
+ @commands = []
19
+ @access_lock = Mutex.new
20
+ end
21
+
22
+ # @param command [SmartCore::Validator::Commands::Base]
23
+ # @return [void]
24
+ #
25
+ # @api private
26
+ # @since 0.1.0
27
+ def add_command(command)
28
+ thread_safe { commands << command }
29
+ end
30
+ alias_method :<<, :add_command
31
+
32
+ # @yield [SmartCore::Validator::Commands::Base]
33
+ # @return [Enumerable]
34
+ #
35
+ # @api private
36
+ # @since 0.1.0
37
+ def each(&block)
38
+ thread_safe { block_given? ? commands.each(&block) : commands.each }
39
+ end
40
+
41
+ # @param command_set [SmartCore::Validator::CommandSet]
42
+ # @return [void]
43
+ #
44
+ # @api private
45
+ # @since 0.1.0
46
+ def concat(command_set)
47
+ thread_safe { commands.concat(command_set.commands) }
48
+ end
49
+
50
+ # @return [void]
51
+ #
52
+ # @api private
53
+ # @since 0.1.0
54
+ def clear
55
+ thread_safe { commands.clear }
56
+ end
57
+
58
+ private
59
+
60
+ # @param block [Proc]
61
+ # @return [Any]
62
+ #
63
+ # @api private
64
+ # @since 0.1.0
65
+ def thread_safe(&block)
66
+ @access_lock.synchronize(&block)
67
+ end
68
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore::Validator::Commands
4
+ # @api private
5
+ # @since 0.1.0
6
+ class AddNestedValidations < Base
7
+ # @since 0.1.0
8
+ include WorkWithNestedsMixin
9
+
10
+ # @return [Symbol, String]
11
+ #
12
+ # @api private
13
+ # @since 0.1.0
14
+ attr_reader :validating_method
15
+
16
+ # @return [Proc]
17
+ #
18
+ # @api private
19
+ # @since 0.1.0
20
+ attr_reader :nested_validations
21
+
22
+ # @param validating_method [Symbol, String]
23
+ # @param nested_validations [Proc]
24
+ # @return [void]
25
+ #
26
+ # @api private
27
+ # @since 0.1.0
28
+ def initialize(validating_method, nested_validations)
29
+ @validating_method = validating_method
30
+ @nested_validations = nested_validations
31
+ end
32
+
33
+ # @param validator [SmartCore::Validator]
34
+ # @return [void]
35
+ #
36
+ # @api private
37
+ # @since 0.1.0
38
+ def call(validator)
39
+ errors = SmartCore::Validator::Invoker.call(validator, validating_method)
40
+
41
+ if errors.empty?
42
+ check_nested_validations(validator, nested_validations)
43
+ else
44
+ validator.__append_errors__(errors)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore::Validator::Commands
4
+ # @api private
5
+ # @since 0.1.0
6
+ class AddValidation < Base
7
+ # @return [Symbol, String]
8
+ #
9
+ # @api private
10
+ # @since 0.1.0
11
+ attr_reader :validating_method
12
+
13
+ # @param validating_method [String, Symbol]
14
+ # @return [void]
15
+ #
16
+ # @api private
17
+ # @since 0.1.0
18
+ def initialize(validating_method)
19
+ @validating_method = validating_method
20
+ end
21
+
22
+ # @param validator [SmartCore::Validator]
23
+ # @return [void]
24
+ #
25
+ # @api private
26
+ # @since 0.1.0
27
+ def call(validator)
28
+ errors = SmartCore::Validator::Invoker.call(validator, validating_method)
29
+ validator.__append_errors__(errors) unless errors.empty?
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Validator::Commands::Base
6
+ # @param validator [SmartCore::Validator]
7
+ # @return [void]
8
+ #
9
+ # @api private
10
+ # @since 0.1.0
11
+ def call(validator); end
12
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore::Validator::Commands
4
+ # @api private
5
+ # @since 0.1.0
6
+ class ValidateWith < Base
7
+ # @since 0.1.0
8
+ include WorkWithNestedsMixin
9
+
10
+ # @return [Class<SmartCore::Validator>]
11
+ #
12
+ # @api private
13
+ # @since 0.1.0
14
+ attr_reader :validating_klass
15
+
16
+ # @return [Proc]
17
+ #
18
+ # @api private
19
+ # @since 0.1.0
20
+ attr_reader :nested_validations
21
+
22
+ # @param validating_klass [Class<SmartCore::Validator>]
23
+ # @param nested_validations [Proc]
24
+ # @return [void]
25
+ #
26
+ # @api private
27
+ # @since 0.1.0
28
+ def initialize(validating_klass, nested_validations)
29
+ @validating_klass = validating_klass
30
+ @nested_validations = nested_validations
31
+ end
32
+
33
+ # @param validator [SmartCore::Validator]
34
+ # @return [void]
35
+ #
36
+ # @api private
37
+ # @since 0.1.0
38
+ def call(validator)
39
+ sub_validator = build_sub_validator(validator, validating_klass)
40
+
41
+ if sub_validator.valid?
42
+ check_nested_validations(validator, nested_validations)
43
+ else
44
+ validator.__append_errors__(sub_validator.__validation_errors__)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Validator::Commands::WorkWithNestedsMixin
6
+ # @param validator [SmartCore::Validator]
7
+ # @param nested_validations [Proc]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def check_nested_validations(validator, nested_validations)
13
+ nested_validator = build_nested_validator(validator, nested_validations)
14
+
15
+ unless nested_validator.valid?
16
+ validator.__append_errors__(nested_validator.__validation_errors__)
17
+ end
18
+ end
19
+
20
+ # @param validator [SmartCore::Validator]
21
+ # @param nested_validations [Proc]
22
+ # @return [SmartCore::Validator]
23
+ #
24
+ # @api private
25
+ # @since 0.1.0
26
+ def build_nested_validator(validator, nested_validations)
27
+ Class.new(validator.class).tap do |klass|
28
+ klass.clear_commands
29
+ klass.instance_eval(&nested_validations)
30
+ end.new(**validator.__attributes__)
31
+ end
32
+
33
+ # @param validator [SmartCore::Validator]
34
+ # @param another_validating_klass [Class<SmartCore::Validator>]
35
+ # @return [SmartCore::Validator]
36
+ #
37
+ # @api private
38
+ # @since 0.1.0
39
+ def build_sub_validator(validator, another_validating_klass)
40
+ another_validating_klass.new(**validator.__attributes__)
41
+ end
42
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore::Validator::Commands
4
+ require_relative 'commands/base'
5
+ require_relative 'commands/work_with_nesteds_mixin'
6
+ require_relative 'commands/add_validation'
7
+ require_relative 'commands/add_nested_validations'
8
+ require_relative 'commands/validate_with'
9
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Validator
6
+ module DSL
7
+ class << self
8
+ # @param base_klass [Class]
9
+ # @return [void]
10
+ #
11
+ # @api private
12
+ # @since 0.1.0
13
+ def extended(base_klass)
14
+ base_klass.instance_variable_set(:@__commands__, CommandSet.new)
15
+ base_klass.instance_variable_set(:@__attributes__, AttributeSet.new)
16
+
17
+ base_klass.singleton_class.prepend(Module.new do
18
+ def inherited(child_klass)
19
+ child_klass.instance_variable_set(:@__commands__, CommandSet.new)
20
+ child_klass.instance_variable_set(:@__attributes__, AttributeSet.new)
21
+
22
+ child_klass.commands.concat(commands)
23
+ child_klass.attributes.concat(attributes)
24
+
25
+ super(child_klass)
26
+ end
27
+ end)
28
+ end
29
+ end
30
+
31
+ # @param attribute_name [String, Symbol]
32
+ # @return [void]
33
+ #
34
+ # @api private
35
+ # @since 0.1.0
36
+ def attribute(attribute_name, default: nil)
37
+ attribute = SmartCore::Validator::Attribute.new(attribute_name, default)
38
+ attributes << attribute
39
+ attr_reader attribute.name
40
+ end
41
+
42
+ # @return [SmartCore::Validator::AttributeSet]
43
+ #
44
+ # @api private
45
+ # @since 0.1.0
46
+ def attributes
47
+ @__attributes__
48
+ end
49
+
50
+ # @return [SmartCore::Validator::CommandSet]
51
+ #
52
+ # @api private
53
+ # @since 0.1.0
54
+ def commands
55
+ @__commands__
56
+ end
57
+
58
+ # @return [void]
59
+ #
60
+ # @api private
61
+ # @since 0.1.0
62
+ def clear_commands
63
+ commands.clear
64
+ end
65
+
66
+ # @param validating_method [Symbol, String]
67
+ # @param nested_validations [Proc]
68
+ # @return [void]
69
+ #
70
+ # @see SmartCore::Validator::Commands::AddValidation
71
+ # @see SmartCore::Validator::Commands::AddNestedValidations
72
+ #
73
+ # @api public
74
+ # @since 0.1.0
75
+ def validate(validating_method, &nested_validations)
76
+ if block_given?
77
+ commands << Commands::AddNestedValidations.new(validating_method, nested_validations)
78
+ else
79
+ commands << Commands::AddValidation.new(validating_method)
80
+ end
81
+ end
82
+
83
+ # @param validating_klass [Class<SmartCore::Validator>]
84
+ # @param nested_validations [Proc]
85
+ # @return [void]
86
+ #
87
+ # @see SmartCore::Validator::Commands::ValidateWith
88
+ #
89
+ # @api private
90
+ # @since 0.1.0
91
+ def validate_with(validating_klass, &nested_validations)
92
+ commands << Commands::ValidateWith.new(validating_klass, nested_validations)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SmartCore::Validator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class ErrorSet
7
+ # @return [void]
8
+ #
9
+ # @api private
10
+ # @since 0.1.0
11
+ def initialize
12
+ @errors = Set.new
13
+ @access_lock = Mutex.new
14
+ end
15
+
16
+ # @param error_codes [Arrray<Symbol>]
17
+ # @return [void]
18
+ #
19
+ # @api private
20
+ # @since 0.1.0
21
+ def add_error(error_code)
22
+ thread_safe { store_error(error_code) }
23
+ end
24
+
25
+ # @param error_set [SmartCore::Validator::ErrorSet]
26
+ # @return [void]
27
+ #
28
+ # @api private
29
+ # @since 0.1.0
30
+ def concat(error_set)
31
+ thread_safe do
32
+ error_set.codes.each do |error_code|
33
+ store_error(error_code)
34
+ end
35
+ end
36
+ end
37
+
38
+ # @return [Boolean]
39
+ #
40
+ # @api private
41
+ # @since 0.1.0
42
+ def empty?
43
+ thread_safe { errors.empty? }
44
+ end
45
+
46
+ # @return [void]
47
+ #
48
+ # @api private
49
+ # @since 0.1.0
50
+ def clear
51
+ thread_safe { errors.clear }
52
+ end
53
+
54
+ # @return [Array<Symbol>]
55
+ #
56
+ # @api private
57
+ # @since 0.1.0
58
+ def codes
59
+ thread_safe { errors.to_a }
60
+ end
61
+
62
+ private
63
+
64
+ # @return [Array<Symbol>]
65
+ #
66
+ # @api private
67
+ # @since 0.1.0
68
+ attr_reader :errors
69
+
70
+ # @param error_code [Symbol, String]
71
+ # @return [void]
72
+ #
73
+ # @raise [SmartCore::Validator::IncorrectErrorCodeError]
74
+ #
75
+ # @api private
76
+ # @since 0.1.0
77
+ def store_error(error_code)
78
+ unless error_code.is_a?(Symbol) || error_code.is_a?(String)
79
+ raise IncorrectErrorCodeError, 'Error code should be a symbol or a string'
80
+ end
81
+
82
+ errors << error_code.to_sym
83
+ end
84
+
85
+ # @param block [Proc]
86
+ # @return [Any]
87
+ #
88
+ # @api private
89
+ # @since 0.1.0
90
+ def thread_safe(&block)
91
+ @access_lock.synchronize(&block)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SmartCore::Validator
4
+ # @api public
5
+ # @since 0.1.0
6
+ Error = Class.new(StandardError)
7
+
8
+ # @api public
9
+ # @since 0.1.0
10
+ IncorrectErrorCodeError = Class.new(Error)
11
+
12
+ # @api public
13
+ # @since 0.1.0
14
+ IncorrectAttributeNameError = Class.new(Error)
15
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SmartCore::Validator
4
+ # @api private
5
+ # @since 0.1.0
6
+ class Invoker
7
+ class << self
8
+ # @param validator [SmartCore::Validator]
9
+ # @param validating_method [Symbol, String]
10
+ # @return [SmartCore::Validator::ErrorSet]
11
+ #
12
+ # @api private
13
+ # @since 0.1.0
14
+ def call(validator, validating_method)
15
+ new(validator).call(validating_method)
16
+ end
17
+ end
18
+
19
+ # @return [SmartCore::Validator]
20
+ #
21
+ # @api private
22
+ # @since 0.1.0k
23
+ attr_reader :validator
24
+
25
+ # @param validator [SmartCore::Validator]
26
+ # @return [void]
27
+ #
28
+ # @api private
29
+ # @since 0.1.0
30
+ def initialize(validator)
31
+ @validator = validator
32
+ @access_lock = Mutex.new
33
+ end
34
+
35
+ # @param validating_method [String, Symbol]
36
+ # @return [SmartCore::Validator::ErrorSet]
37
+ #
38
+ # @api private
39
+ # @since 0.1.0
40
+ def call(validating_method)
41
+ thread_safe do
42
+ ErrorSet.new.tap do |outer_errors|
43
+ extended_validator(outer_errors).send(validating_method)
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ # Creates new validator object cloned from the original validator object
51
+ # with the new functionality: error code interception.
52
+ #
53
+ # @param outer_errors [SmartCore::Validator::ErrorSet]
54
+ # @return [SmartCore::Validator]
55
+ #
56
+ # @api private
57
+ # @since 0.1.0
58
+ def extended_validator(outer_errors)
59
+ validator.dup.tap do |validator_clone|
60
+ validator_clone.define_singleton_method(:error) do |error_code|
61
+ outer_errors.add_error(error_code)
62
+ end
63
+ end
64
+ end
65
+
66
+ # @param block [Proc]
67
+ # @return [Any]
68
+ #
69
+ # @api private
70
+ # @since 0.1.0
71
+ def thread_safe(&block)
72
+ @access_lock.synchronize(&block)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.1.0
5
+ class SmartCore::Validator
6
+ require_relative 'validator/exceptions'
7
+ require_relative 'validator/command_set'
8
+ require_relative 'validator/attribute'
9
+ require_relative 'validator/attribute_set'
10
+ require_relative 'validator/error_set'
11
+ require_relative 'validator/invoker'
12
+ require_relative 'validator/commands'
13
+ require_relative 'validator/dsl'
14
+
15
+ # @since 0.1.0
16
+ extend DSL
17
+
18
+ class << self
19
+ # @param argumants [Any]
20
+ # @param options [Hash<Symbol, Object>]
21
+ # @return [void]
22
+ #
23
+ # @api public
24
+ # @since 0.1.0
25
+ def new(*arguments, **options)
26
+ allocate.tap do |object|
27
+ object.instance_variable_set(:@__validation_errors__, ErrorSet.new)
28
+ object.instance_variable_set(:@__invokation_lock__, Mutex.new)
29
+ object.instance_variable_set(:@__access_lock__, Mutex.new)
30
+
31
+ attributes.each do |attribute|
32
+ attribute_name = attribute.name
33
+
34
+ attribute_value =
35
+ if options.key?(attribute_name)
36
+ options[attribute_name]
37
+ else
38
+ attribute.default_value
39
+ end
40
+
41
+ object.instance_variable_set("@#{attribute_name}", attribute_value)
42
+ end
43
+
44
+ object.send(:initialize, *arguments, **options)
45
+ end
46
+ end
47
+ end
48
+
49
+ # @return [SmartCore::Validator::ErrorSet]
50
+ #
51
+ # @api private
52
+ # @since 0.1.0
53
+ attr_reader :__validation_errors__
54
+
55
+ # @return [void]
56
+ #
57
+ # @api public
58
+ # @since 0.1.0
59
+ def initialize(*, **); end
60
+
61
+ # @return [Boolean]
62
+ #
63
+ # @api public
64
+ # @since 0.1.0
65
+ def valid?
66
+ __thread_safe_invokation__ do
67
+ __validation_errors__.clear
68
+ self.class.commands.each { |command| command.call(self) }
69
+ __validation_errors__.empty?
70
+ end
71
+ end
72
+
73
+ # @return [Array<Symbol>]
74
+ #
75
+ # @api public
76
+ # @since 0.1.0
77
+ def errors
78
+ __thread_safe_access__ do
79
+ __validation_errors__.codes
80
+ end
81
+ end
82
+
83
+ # @param error_set [SmartCore::Validator::ErrorSet]
84
+ # @return [void]
85
+ #
86
+ # @api private
87
+ # @since 0.1.0
88
+ def __append_errors__(error_set)
89
+ __thread_safe_access__ do
90
+ __validation_errors__.concat(error_set)
91
+ end
92
+ end
93
+
94
+ # @return [Hash<Symbol, Object>]
95
+ #
96
+ # @api private
97
+ # @since 0.1.0
98
+ def __attributes__
99
+ __thread_safe_access__ do
100
+ self.class.attributes.each_with_object({}) do |attribute, accumulator|
101
+ accumulator[attribute.name] = instance_variable_get("@#{attribute.name}")
102
+ end
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ # @param block [Proc]
109
+ # @return [Any]
110
+ #
111
+ # @api private
112
+ # @since 0.1.0
113
+ def __thread_safe_invokation__(&block)
114
+ @__invokation_lock__.synchronize(&block)
115
+ end
116
+
117
+ # @param block [Proc]
118
+ # @return [Any]
119
+ #
120
+ # @api private
121
+ # @since 0.1.0
122
+ def __thread_safe_access__(&block)
123
+ @__access_lock__.synchronize(&block)
124
+ end
125
+ end
@@ -1,3 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SmartCore
2
- VERSION = "0.0.0"
4
+ # @return [String]
5
+ #
6
+ # @api public
7
+ # @since 0.1.0
8
+ VERSION = '0.1.0'
3
9
  end
data/lib/smart_core.rb CHANGED
@@ -1,5 +1,8 @@
1
- require "smart_core/version"
1
+ # frozen_string_literal: true
2
2
 
3
+ # @api public
4
+ # @since 0.1.0
3
5
  module SmartCore
4
- # Your code goes here...
6
+ require_relative 'smart_core/version'
7
+ require_relative 'smart_core/validator'
5
8
  end
data/smart_core.gemspec CHANGED
@@ -1,29 +1,34 @@
1
+ # frozen_string_literal: true
1
2
 
2
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "smart_core/version"
5
+ require 'smart_core/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "smart_core"
8
+ spec.required_ruby_version = '>= 2.3.8'
9
+
10
+ spec.name = 'smart_core'
8
11
  spec.version = SmartCore::VERSION
9
- spec.authors = ["Rustam Ibragimov"]
10
- spec.email = ["iamdaiver@icloud.com"]
12
+ spec.authors = ['Rustam Ibragimov']
13
+ spec.email = ['iamdaiver@icloud.com']
14
+ spec.summary = '(in active development) A set of common abstractions'
15
+ spec.description = '(in active development) A set of common abstractions'
16
+ spec.homepage = 'https://github.com/0exp/smart_core'
17
+ spec.license = 'MIT'
11
18
 
12
- spec.summary = 'Soon'
13
- spec.description = 'Soon'
14
- spec.homepage = "https://github.com/0exp/smart_core"
15
- spec.license = "MIT"
19
+ spec.bindir = 'bin'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
16
22
 
17
- # Specify which files should be added to the gem when it is released.
18
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|features)/}) }
21
25
  end
22
- spec.bindir = "exe"
23
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
- spec.require_paths = ["lib"]
25
26
 
26
- spec.add_development_dependency "bundler", "~> 1.16"
27
- spec.add_development_dependency "rake", "~> 10.0"
28
- spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency 'coveralls', '~> 0.8.22'
28
+ spec.add_development_dependency 'simplecov', '~> 0.16.1'
29
+ spec.add_development_dependency 'armitage-rubocop', '~> 0.16.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.8.0'
31
+ spec.add_development_dependency 'bundler', '~> 1.17'
32
+ spec.add_development_dependency 'rake', '~> 12.3'
33
+ spec.add_development_dependency 'pry', '~> 0.12'
29
34
  end
metadata CHANGED
@@ -1,58 +1,114 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Ibragimov
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-26 00:00:00.000000000 Z
11
+ date: 2019-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: coveralls
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.22
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.22
27
+ - !ruby/object:Gem::Dependency
28
+ name: simplecov
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.16.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.16.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: armitage-rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.16.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.16.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.8.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.8.0
13
69
  - !ruby/object:Gem::Dependency
14
70
  name: bundler
15
71
  requirement: !ruby/object:Gem::Requirement
16
72
  requirements:
17
73
  - - "~>"
18
74
  - !ruby/object:Gem::Version
19
- version: '1.16'
75
+ version: '1.17'
20
76
  type: :development
21
77
  prerelease: false
22
78
  version_requirements: !ruby/object:Gem::Requirement
23
79
  requirements:
24
80
  - - "~>"
25
81
  - !ruby/object:Gem::Version
26
- version: '1.16'
82
+ version: '1.17'
27
83
  - !ruby/object:Gem::Dependency
28
84
  name: rake
29
85
  requirement: !ruby/object:Gem::Requirement
30
86
  requirements:
31
87
  - - "~>"
32
88
  - !ruby/object:Gem::Version
33
- version: '10.0'
89
+ version: '12.3'
34
90
  type: :development
35
91
  prerelease: false
36
92
  version_requirements: !ruby/object:Gem::Requirement
37
93
  requirements:
38
94
  - - "~>"
39
95
  - !ruby/object:Gem::Version
40
- version: '10.0'
96
+ version: '12.3'
41
97
  - !ruby/object:Gem::Dependency
42
- name: rspec
98
+ name: pry
43
99
  requirement: !ruby/object:Gem::Requirement
44
100
  requirements:
45
101
  - - "~>"
46
102
  - !ruby/object:Gem::Version
47
- version: '3.0'
103
+ version: '0.12'
48
104
  type: :development
49
105
  prerelease: false
50
106
  version_requirements: !ruby/object:Gem::Requirement
51
107
  requirements:
52
108
  - - "~>"
53
109
  - !ruby/object:Gem::Version
54
- version: '3.0'
55
- description: Soon
110
+ version: '0.12'
111
+ description: "(in active development) A set of common abstractions"
56
112
  email:
57
113
  - iamdaiver@icloud.com
58
114
  executables: []
@@ -61,7 +117,9 @@ extra_rdoc_files: []
61
117
  files:
62
118
  - ".gitignore"
63
119
  - ".rspec"
120
+ - ".rubocop.yml"
64
121
  - ".travis.yml"
122
+ - CHANGELOG.md
65
123
  - CODE_OF_CONDUCT.md
66
124
  - Gemfile
67
125
  - LICENSE.txt
@@ -70,6 +128,20 @@ files:
70
128
  - bin/console
71
129
  - bin/setup
72
130
  - lib/smart_core.rb
131
+ - lib/smart_core/validator.rb
132
+ - lib/smart_core/validator/attribute.rb
133
+ - lib/smart_core/validator/attribute_set.rb
134
+ - lib/smart_core/validator/command_set.rb
135
+ - lib/smart_core/validator/commands.rb
136
+ - lib/smart_core/validator/commands/add_nested_validations.rb
137
+ - lib/smart_core/validator/commands/add_validation.rb
138
+ - lib/smart_core/validator/commands/base.rb
139
+ - lib/smart_core/validator/commands/validate_with.rb
140
+ - lib/smart_core/validator/commands/work_with_nesteds_mixin.rb
141
+ - lib/smart_core/validator/dsl.rb
142
+ - lib/smart_core/validator/error_set.rb
143
+ - lib/smart_core/validator/exceptions.rb
144
+ - lib/smart_core/validator/invoker.rb
73
145
  - lib/smart_core/version.rb
74
146
  - smart_core.gemspec
75
147
  homepage: https://github.com/0exp/smart_core
@@ -84,16 +156,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
84
156
  requirements:
85
157
  - - ">="
86
158
  - !ruby/object:Gem::Version
87
- version: '0'
159
+ version: 2.3.8
88
160
  required_rubygems_version: !ruby/object:Gem::Requirement
89
161
  requirements:
90
162
  - - ">="
91
163
  - !ruby/object:Gem::Version
92
164
  version: '0'
93
165
  requirements: []
94
- rubyforge_project:
95
- rubygems_version: 2.7.6
166
+ rubygems_version: 3.0.1
96
167
  signing_key:
97
168
  specification_version: 4
98
- summary: Soon
169
+ summary: "(in active development) A set of common abstractions"
99
170
  test_files: []