attributary 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3ebdff0c364b0480e2c79d90df50064f2b015400
4
+ data.tar.gz: cef7eccd653b8c77ea68aa3f08a40ab89787ab63
5
+ SHA512:
6
+ metadata.gz: 0aa7ee051f7a870076ff468b1f6b1a9b4ab91a1342e2e3c5f876c3fb7a024affd4b8a3b279cc933a2a340161444082f9702025a4d6b4e725d3682f747a707e51
7
+ data.tar.gz: d13c1daefa1c490e0a9fe70ac1dfbb626c477525716013bf1467212688b7c1f72b17821b857fa7679b5c05a730db844da493fa8923464e5556181199ee56a3b3
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in attributary.gemspec
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # Attributary
2
+
3
+ Like `ActiveModel::Attributes` but not. No dependencies.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'attributary'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install attributary
20
+
21
+ ## Usage
22
+
23
+ ### Configuration
24
+
25
+ Attributary.configure do |c|
26
+ c.dsl_name = :donkey # defaults to :attrubute
27
+ c.strict_mode = false # set this to `true` to skip casting in the writer method (raises TypeError)
28
+ end
29
+
30
+ ### Actually using it
31
+
32
+ class Character
33
+ include Attributary::DSL
34
+
35
+ donkey :gender, :symbol, default: :male
36
+ end
37
+
38
+ character = Character.new
39
+ puts character.gender # :male
40
+ character.gender = 'female'
41
+ puts character.gender # :female
42
+
43
+ ### Initializing
44
+
45
+ Want to initialize? Cool.
46
+
47
+ class Character
48
+ include Attributary::Initializer
49
+ include Attributary::DSL
50
+
51
+ donkey :gender, :symbol, default: :male
52
+ end
53
+
54
+ character = Character.new(gender: :male)
55
+ puts character.gender # :male
56
+
57
+ Have to initialize other stuff? Send a hash to attributary_initialize
58
+
59
+ class Character
60
+ include Attributary::Initializer
61
+ include Attributary::DSL
62
+
63
+ donkey :gender, :symbol, default: :male
64
+
65
+ def initialize(name, attributary_attributes = {})
66
+ @name = name
67
+ attributary_initialize(attributary_attributes)
68
+ end
69
+ end
70
+
71
+ character = Character.new("Charlie", gender: :male)
72
+ puts character.gender # :male
73
+
74
+ ### Validations
75
+
76
+ There are two types of validations. You can pass a `:collection` option, and/or you can pass a `:validates` option.
77
+
78
+ #### Using `:collection`
79
+
80
+ Simply pass an array of the possible options for the attribute.
81
+
82
+ class Character
83
+ include Attributary::DSL
84
+
85
+ donkey :gender, :symbol, default: :male, collection: [:male, :female]
86
+ end
87
+
88
+ character = Character.new("Charlie", gender: :male)
89
+ puts character.gender # :male
90
+ character.gender = :left # raises CollectionValidationError
91
+ character.gender = :female
92
+ puts character.gender # :female
93
+ character.gender = 'male' # gets casted correctly, and doesn't raise an error. Turn this off with strict_mode
94
+
95
+ #### Using `:validates`
96
+
97
+ Pass a proc or a method name.
98
+
99
+ class Character
100
+ include Attributary::DSL
101
+
102
+ donkey :gender, :symbol, default: :male, validates: proc { |value| [:male, :female].include?(value) }
103
+ end
104
+
105
+ character = Character.new("Charlie", gender: :male)
106
+ puts character.gender # :male
107
+ character.gender = :left # raises CollectionValidationError
108
+ character.gender = 'female' # gets casted correctly, and doesn't raise an error
109
+
110
+
111
+ ### Types
112
+
113
+ Supports some really naive typecasting. [See the supported types](https://github.com/joshmn/attributary/tree/master/lib/attributary/types). Create your own by inheriting from `Attributary::Types` and naming it like `ClassNameType`
114
+
115
+ module Attributary
116
+ module Types
117
+ class FriendType < Type
118
+ def self.cast_to(value)
119
+ Foe.new(value)
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ ## Contributing
126
+
127
+ Bug reports and pull requests are welcome on GitHub at https://github.com/joshmn/attributary
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ task default: :spec
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'attributary/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'attributary'
7
+ spec.version = Attributary::VERSION
8
+ spec.authors = ['Josh Brody']
9
+ spec.email = ['josh@josh.mn']
10
+
11
+ spec.summary = 'Like ActiveModel::Attributes but less fluffy and more attribute-y.'
12
+ spec.description = 'Like `ActiveModel::Attributes` but less fluffy and more attribute-y.'
13
+ spec.homepage = 'https://github.com/joshmn/attributary'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.15'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'attributary'
5
+
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__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,9 @@
1
+ require_relative 'attributary/config'
2
+ require_relative 'attributary/core_ext'
3
+ require_relative 'attributary/type'
4
+ require_relative 'attributary/initializer'
5
+ require_relative 'attributary/dsl'
6
+ require_relative 'attributary/version'
7
+
8
+ module Attributary
9
+ end
Binary file
@@ -0,0 +1,27 @@
1
+ module Attributary
2
+ def self.configuration
3
+ @configuration || Config.new
4
+ end
5
+
6
+ def self.configuration=(val)
7
+ @configuration = val
8
+ end
9
+
10
+ def self.configure
11
+ self.configuration ||= Config.new
12
+ yield(configuration)
13
+ end
14
+
15
+ class Config
16
+ attr_accessor :dsl_name, :strict_mode
17
+
18
+ def initialize
19
+ @dsl_name = :attribute
20
+ @strict_mode = false
21
+ end
22
+
23
+ def strict_mode?
24
+ @strict_mode
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ class String
2
+ unless String.respond_to?(:constantize)
3
+ def constantize
4
+ Object.const_get(self)
5
+ end
6
+ end
7
+
8
+ unless String.respond_to?(:safe_constantize)
9
+ def safe_constantize
10
+ constantize
11
+ rescue StandardError
12
+ nil
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,87 @@
1
+ module Attributary
2
+ module DSL
3
+ class ValidationError < StandardError; end
4
+ class CollectionValidationError < StandardError; end
5
+
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def _attributary_attribute_set
12
+ @_attributary_attribute_set ||= {}
13
+ end
14
+
15
+ def _attributary_attributes
16
+ hash = {}
17
+ _attributary_attribute_set.keys.each do |k|
18
+ hash[k] = instance_variable_get(:"@#{k}")
19
+ end
20
+ hash
21
+ end
22
+
23
+ define_method(Attributary.configuration.dsl_name) do |name, type, options = {}|
24
+ options[:type] = type
25
+ _attributary_attribute_set[name] = options
26
+ _attributary_reader(name, type, options)
27
+ _attributary_writer(name, type, options)
28
+ end
29
+
30
+ def _attributary_reader(name, type, options)
31
+ define_method(name) do
32
+ instance_variable_get(:"@#{name}") || if options[:default].is_a?(Proc)
33
+ options[:default].call
34
+ else
35
+ options[:default]
36
+ end
37
+ end
38
+ if type == :boolean
39
+ define_method("#{name}?") do
40
+ send(name.to_s)
41
+ end
42
+ end
43
+ end
44
+
45
+ def _attributary_writer(name, type, options)
46
+ define_method("#{name}=") do |value|
47
+ value = self.class._attributary_cast_to(type, value) unless Attributary.configuration.strict_mode?
48
+ self.class._attributary_check_collection(value, options[:collection]) if options[:collection].is_a?(Array)
49
+ self.class._attributary_validate_attribute(value, options[:validates])
50
+ instance_variable_set(:"@#{name}", value)
51
+ end
52
+ end
53
+
54
+ def _attributary_check_collection(value, collection)
55
+ unless collection.include?(value)
56
+ raise CollectionValidationError, "Value `#{value}' is not in the collection #{collection}."
57
+ end
58
+ end
59
+
60
+ def _attributary_validate_attribute(value, validator)
61
+ return true if validator.nil?
62
+ raise ValidationError, 'Validator failed.' unless validator.call(value)
63
+ end
64
+
65
+ def _attributary_cast_to(type, value)
66
+ cast_klass = _attributary_cast_class(type)
67
+ cast_klass.cast_to(value)
68
+ end
69
+
70
+ def _attributary_cast_class(type)
71
+ cast_klass_name = _attributary_cast_class_name(type)
72
+ cast_klass = cast_klass_name.safe_constantize
73
+ if cast_klass.nil?
74
+ raise NameError, "#{cast_klass_name} is not a valid type."
75
+ end
76
+ unless cast_klass.respond_to?(:cast_to)
77
+ raise NoMethodError, "#{cast_klass} should have a class-method of cast_to"
78
+ end
79
+ cast_klass
80
+ end
81
+
82
+ def _attributary_cast_class_name(type)
83
+ "Attributary::Types::#{type.to_s.split('_').map(&:capitalize).join}Type"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,13 @@
1
+ module Attributary
2
+ module Initializer
3
+ def initialize(options = {})
4
+ attributary_initialize(options)
5
+ end
6
+
7
+ def attributary_initialize(options = {})
8
+ options.each do |k, v|
9
+ send("#{k}=", v) if attribute_set[k]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module Attributary
2
+ module Types
3
+ class Type
4
+ def self.cast_to(value)
5
+ value
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ require_relative 'types/array_type'
12
+ require_relative 'types/big_decimal_type'
13
+ require_relative 'types/boolean_type'
14
+ require_relative 'types/float_type'
15
+ require_relative 'types/hash_type'
16
+ require_relative 'types/integer_type'
17
+ require_relative 'types/string_type'
18
+ require_relative 'types/symbol_type'
@@ -0,0 +1,9 @@
1
+ module Attributary
2
+ module Types
3
+ class ArrayType < Type
4
+ def self.cast_to(value)
5
+ value.to_a
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Attributary
2
+ module Types
3
+ class BigDecimal < Type
4
+ def self.cast_to(value)
5
+ BigDecimal(value.to_s)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Attributary
2
+ module Types
3
+ class BooleanType < Type
4
+ def self.cast_to(value)
5
+ if ['false', 0, '0', false, nil, ''].include?(value)
6
+ false
7
+ else
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Attributary
2
+ module Types
3
+ class FloatType < Type
4
+ def self.cast_to(value)
5
+ value.to_f
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Attributary
2
+ module Types
3
+ class HashType < Type
4
+ def self.cast_to(value)
5
+ value.is_a?(Hash) ? value : value.to_h
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Attributary
2
+ module Types
3
+ class IntegerType < Type
4
+ def self.cast_to(value)
5
+ value.to_i
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Attributary
2
+ module Types
3
+ class StringType < Type
4
+ def self.cast_to(value)
5
+ value.to_s
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Attributary
2
+ module Types
3
+ class SymbolType < Type
4
+ def self.cast_to(value)
5
+ value.to_s.to_sym
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Attributary
2
+ VERSION = '0.1.0'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attributary
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Brody
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Like `ActiveModel::Attributes` but less fluffy and more attribute-y.
42
+ email:
43
+ - josh@josh.mn
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - README.md
51
+ - Rakefile
52
+ - attributary.gemspec
53
+ - bin/console
54
+ - bin/setup
55
+ - lib/.DS_Store
56
+ - lib/attributary.rb
57
+ - lib/attributary/.DS_Store
58
+ - lib/attributary/config.rb
59
+ - lib/attributary/core_ext.rb
60
+ - lib/attributary/dsl.rb
61
+ - lib/attributary/initializer.rb
62
+ - lib/attributary/type.rb
63
+ - lib/attributary/types/array_type.rb
64
+ - lib/attributary/types/big_decimal_type.rb
65
+ - lib/attributary/types/boolean_type.rb
66
+ - lib/attributary/types/float_type.rb
67
+ - lib/attributary/types/hash_type.rb
68
+ - lib/attributary/types/integer_type.rb
69
+ - lib/attributary/types/string_type.rb
70
+ - lib/attributary/types/symbol_type.rb
71
+ - lib/attributary/version.rb
72
+ homepage: https://github.com/joshmn/attributary
73
+ licenses: []
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.6.13
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Like ActiveModel::Attributes but less fluffy and more attribute-y.
95
+ test_files: []