soulless 0.6.0 → 1.0.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -18
  3. data/.rspec +1 -1
  4. data/.travis.yml +7 -12
  5. data/CODE_OF_CONDUCT.md +49 -0
  6. data/Gemfile +17 -13
  7. data/LICENSE.txt +17 -18
  8. data/README.md +8 -181
  9. data/Rakefile +4 -8
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/docker-compose.yml +11 -0
  13. data/lib/soulless/attributes.rb +33 -0
  14. data/lib/soulless/callbacks.rb +13 -0
  15. data/lib/soulless/dirty.rb +10 -8
  16. data/lib/soulless/inheritance.rb +120 -124
  17. data/lib/soulless/model.rb +24 -63
  18. data/lib/soulless/validations/uniqueness_validator.rb +7 -8
  19. data/lib/soulless/validations.rb +1 -2
  20. data/lib/soulless/version.rb +1 -1
  21. data/lib/soulless.rb +5 -23
  22. data/soulless.gemspec +12 -13
  23. metadata +30 -173
  24. data/lib/soulless/accessors.rb +0 -51
  25. data/lib/soulless/associations.rb +0 -35
  26. data/lib/soulless/locale/en.yml +0 -30
  27. data/lib/soulless/railtie.rb +0 -7
  28. data/lib/soulless/validations/associated_validator.rb +0 -17
  29. data/spec/associated_validator_spec.rb +0 -31
  30. data/spec/associations_spec.rb +0 -65
  31. data/spec/dirty_spec.rb +0 -54
  32. data/spec/dummy/.gitignore +0 -17
  33. data/spec/dummy/README.rdoc +0 -28
  34. data/spec/dummy/Rakefile +0 -6
  35. data/spec/dummy/app/assets/images/.keep +0 -0
  36. data/spec/dummy/app/assets/javascripts/application.js +0 -16
  37. data/spec/dummy/app/assets/stylesheets/application.css +0 -13
  38. data/spec/dummy/app/controllers/application_controller.rb +0 -5
  39. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  40. data/spec/dummy/app/forms/dummy_form.rb +0 -7
  41. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  42. data/spec/dummy/app/mailers/.keep +0 -0
  43. data/spec/dummy/app/models/.keep +0 -0
  44. data/spec/dummy/app/models/concerns/.keep +0 -0
  45. data/spec/dummy/app/models/dummy_model.rb +0 -6
  46. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  47. data/spec/dummy/bin/bundle +0 -3
  48. data/spec/dummy/bin/rails +0 -4
  49. data/spec/dummy/bin/rake +0 -4
  50. data/spec/dummy/config/application.rb +0 -28
  51. data/spec/dummy/config/boot.rb +0 -6
  52. data/spec/dummy/config/database.yml +0 -25
  53. data/spec/dummy/config/environment.rb +0 -5
  54. data/spec/dummy/config/environments/development.rb +0 -29
  55. data/spec/dummy/config/environments/production.rb +0 -80
  56. data/spec/dummy/config/environments/test.rb +0 -36
  57. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  58. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  59. data/spec/dummy/config/initializers/inflections.rb +0 -16
  60. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  61. data/spec/dummy/config/initializers/secret_token.rb +0 -12
  62. data/spec/dummy/config/initializers/session_store.rb +0 -3
  63. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  64. data/spec/dummy/config/locales/en.yml +0 -23
  65. data/spec/dummy/config/routes.rb +0 -56
  66. data/spec/dummy/config.ru +0 -4
  67. data/spec/dummy/db/migrate/20131106221200_create_dummy_models.rb +0 -11
  68. data/spec/dummy/db/schema.rb +0 -24
  69. data/spec/dummy/db/seeds.rb +0 -7
  70. data/spec/dummy/db/test.sqlite3 +0 -0
  71. data/spec/dummy/lib/assets/.keep +0 -0
  72. data/spec/dummy/lib/tasks/.keep +0 -0
  73. data/spec/dummy/log/.keep +0 -0
  74. data/spec/dummy/public/404.html +0 -58
  75. data/spec/dummy/public/422.html +0 -58
  76. data/spec/dummy/public/500.html +0 -57
  77. data/spec/dummy/public/favicon.ico +0 -0
  78. data/spec/dummy/public/robots.txt +0 -5
  79. data/spec/dummy/vendor/assets/javascripts/.keep +0 -0
  80. data/spec/dummy/vendor/assets/stylesheets/.keep +0 -0
  81. data/spec/inheritance_spec.rb +0 -51
  82. data/spec/soulless_spec.rb +0 -61
  83. data/spec/spec_helper.rb +0 -38
  84. data/spec/support/dummy_association.rb +0 -30
  85. data/spec/support/dummy_class.rb +0 -20
  86. data/spec/support/dummy_model_inheritance.rb +0 -10
  87. data/spec/support/dummy_soulless_inheritance.rb +0 -10
  88. data/spec/support/en.yml +0 -8
  89. data/spec/uniqueness_validator_spec.rb +0 -17
@@ -1,139 +1,135 @@
1
1
  module Soulless
2
2
  module Inheritance
3
- def self.included(base)
4
- base.instance_eval do
5
- def inherit_from(klass, options = {})
6
- attributes = get_attributes(klass, options)
3
+ def inherit_from(klass, options = {})
4
+ attributes = get_attributes(klass, options)
7
5
 
8
- attributes.each do |attribute|
9
- self.attribute(attribute[:name], attribute[:primitive], attribute[:options])
10
- if Object.const_defined?('ActiveModel'.to_sym) && klass.ancestors.include?(ActiveModel::Validations)
11
- setup_validators(attribute[:name], klass, options)
12
- end
13
- end
14
- end
6
+ attributes.each do |attribute|
7
+ self.attribute(attribute[:name], attribute[:primitive], attribute[:options])
8
+ if Object.const_defined?('ActiveModel'.to_sym) && klass.ancestors.include?(ActiveModel::Validations)
9
+ setup_validators(attribute[:name], klass, options)
10
+ end
11
+ end
12
+ end
15
13
 
16
- private
17
- def get_attributes(klass, options)
18
- if klass.ancestors.include?(Virtus::Model::Core)
19
- get_virtus_attributes(klass, options)
20
- elsif Object.const_defined?('ActiveRecord'.to_sym) && klass.ancestors.include?(ActiveRecord::Base)
21
- get_active_record_attributes(klass, options)
22
- end
23
- end
14
+ def get_attributes(klass, options)
15
+ if klass.ancestors.include?(Virtus::Model::Core)
16
+ get_virtus_attributes(klass, options)
17
+ elsif Object.const_defined?('ActiveRecord'.to_sym) && klass.ancestors.include?(ActiveRecord::Base)
18
+ get_active_record_attributes(klass, options)
19
+ end
20
+ end
24
21
 
25
- def get_virtus_attributes(klass, options)
26
- attributes = []
27
- attribute_set = klass.attribute_set
28
- attribute_names = get_attribute_names(attribute_set.map{ |a| a.options[:name] }, options)
29
- attribute_names.each do |attribute_name|
30
- attribute = attribute_set[attribute_name]
31
- if include_attribute?(attribute_name, options)
32
- attributes << {
33
- name: attribute_name,
34
- primitive: attribute.primitive,
35
- options: {
36
- default: attribute.default_value
37
- }
38
- }
39
- end
40
- end
41
- attributes
42
- end
22
+ def get_virtus_attributes(klass, options)
23
+ attributes = []
24
+ attribute_set = klass.attribute_set
25
+ attribute_names = get_attribute_names(attribute_set.map{ |a| a.options[:name] }, options)
26
+ attribute_names.each do |attribute_name|
27
+ attribute = attribute_set[attribute_name]
28
+ if include_attribute?(attribute_name, options)
29
+ attributes << {
30
+ name: attribute_name,
31
+ primitive: attribute.primitive,
32
+ options: {
33
+ default: attribute.default_value
34
+ }
35
+ }
36
+ end
37
+ end
38
+ attributes
39
+ end
43
40
 
44
- def get_active_record_attributes(klass, options)
45
- attributes = []
46
- attribute_names = get_attribute_names(klass.attribute_names.dup, options)
47
- attribute_names.each do |attribute_name|
48
- if include_attribute?(attribute_name, options)
49
- column = klass.columns_hash[attribute_name.to_s]
50
- attribute = {
51
- name: attribute_name,
52
- primitive: String,
53
- options: {}
54
- }
55
- attribute[:primitive] = translate_primitive(column.type) if column
56
- attribute[:options] = { default: column.default } if column &&
57
- (options[:use_database_default] == true ||
58
- options[:use_database_default] == attribute_name ||
59
- (options[:use_database_default].kind_of?(Array) &&
60
- options[:use_database_default].collect { |v| v.to_s }.include?(attribute_name)))
61
- attributes << attribute
62
- end
63
- end
64
- attributes
65
- end
41
+ def get_active_record_attributes(klass, options)
42
+ attributes = []
43
+ attribute_names = get_attribute_names(klass.attribute_names.dup, options)
44
+ attribute_names.each do |attribute_name|
45
+ if include_attribute?(attribute_name, options)
46
+ column = klass.columns_hash[attribute_name.to_s]
47
+ attribute = {
48
+ name: attribute_name,
49
+ primitive: String,
50
+ options: {}
51
+ }
52
+ attribute[:primitive] = translate_primitive(column.type) if column
53
+ attribute[:options] = { default: column.default } if column &&
54
+ (options[:use_database_default] == true ||
55
+ options[:use_database_default] == attribute_name ||
56
+ (options[:use_database_default].kind_of?(Array) &&
57
+ options[:use_database_default].collect { |v| v.to_s }.include?(attribute_name)))
58
+ attributes << attribute
59
+ end
60
+ end
61
+ attributes
62
+ end
66
63
 
67
- def setup_validators(attribute_name, klass, options)
68
- return if skip_validators?(attribute_name, options) || !include_attribute?(attribute_name, options)
69
- klass.validators.each do |validator|
70
- if validator.attributes.include?(attribute_name.to_sym)
71
- validator_class = validator.class
72
- validator_options = {}
73
- validator_options.merge!(validator.options)
74
- next if validator_options.include?(:on) && options["validate_#{attribute_name}_on".to_sym] != validator_options[:on]
75
- validator_options.delete(:on)
76
- if validator_class == ActiveRecord::Validations::UniquenessValidator
77
- validator_class = Validations::UniquenessValidator
78
- validator_options.merge!(model: klass)
79
- else
80
- # Validators from models are ActiveRecord
81
- # objects which aren't useable in an
82
- # environment without a database. It's
83
- # necessary to convert to an ActiveModel
84
- # validation.
85
- if validator_class.name =~ /\AActiveRecord/
86
- validator_class = "ActiveModel::Validations::#{validator_class.name.split('::').last}".constantize
87
- end
88
- end
89
- validates_with(validator_class, { attributes: attribute_name }.merge(validator_options))
90
- end
91
- end
92
- end
64
+ def setup_validators(attribute_name, klass, options)
65
+ return if skip_validators?(attribute_name, options) || !include_attribute?(attribute_name, options)
66
+ klass.validators.each do |validator|
67
+ if validator.attributes.include?(attribute_name.to_sym)
68
+ validator_class = validator.class
69
+ validator_options = {}
70
+ validator_options.merge!(validator.options)
71
+ next if validator_options.include?(:on) && options["validate_#{attribute_name}_on".to_sym] != validator_options[:on]
72
+ validator_options.delete(:on)
73
+ if validator_class.name == 'ActiveRecord::Validations::UniquenessValidator'
74
+ validator_class = Validations::UniquenessValidator
75
+ validator_options.merge!(model: klass)
76
+ else
77
+ # Validators from models are ActiveRecord
78
+ # objects which aren't useable in an
79
+ # environment without a database. It's
80
+ # necessary to convert to an ActiveModel
81
+ # validation.
82
+ if validator_class.name =~ /\AActiveRecord/
83
+ validator_class = "ActiveModel::Validations::#{validator_class.name.split('::').last}".constantize
84
+ end
85
+ end
86
+ validates_with(validator_class, { attributes: attribute_name }.merge(validator_options))
87
+ end
88
+ end
89
+ end
93
90
 
94
- def get_attribute_names(attributes, options)
95
- attribute_names = attributes
96
- attribute_names << options[:additional_attributes] if options[:additional_attributes]
97
- attribute_names.flatten!
98
- attribute_names.map!{ |a| a.to_s }
99
- attribute_names
100
- end
91
+ def get_attribute_names(attributes, options)
92
+ attribute_names = attributes
93
+ attribute_names << options[:additional_attributes] if options[:additional_attributes]
94
+ attribute_names.flatten!
95
+ attribute_names.map!{ |a| a.to_s }
96
+ attribute_names
97
+ end
101
98
 
102
- def translate_primitive(primitive)
103
- return nil unless primitive
104
- translated_primitive = primitive.to_s.capitalize
105
- translated_primitive = Virtus::Attribute::Boolean if translated_primitive == 'Boolean'
106
- translated_primitive = DateTime if translated_primitive == 'Datetime'
107
- translated_primitive = String if translated_primitive == 'Uuid'
108
- translated_primitive = BigDecimal if translated_primitive == 'Decimal'
109
- translated_primitive
110
- end
99
+ def translate_primitive(primitive)
100
+ return nil unless primitive
101
+ translated_primitive = primitive.to_s.capitalize
102
+ translated_primitive = Virtus::Attribute::Boolean if translated_primitive == 'Boolean'
103
+ translated_primitive = DateTime if translated_primitive == 'Datetime'
104
+ translated_primitive = String if translated_primitive == 'Uuid'
105
+ translated_primitive = String if translated_primitive == 'Citext'
106
+ translated_primitive = BigDecimal if translated_primitive == 'Decimal'
107
+ translated_primitive
108
+ end
111
109
 
112
- def include_attribute?(attribute_name, options)
113
- # Attributes we don't want to inherit
114
- exclude_attributes = ['id']
115
- exclude_attributes << ['created_at', 'updated_at'] unless options[:include_timestamps]
116
- exclude_attributes << options[:exclude] if options[:exclude]
117
- exclude_attributes.flatten!
118
- exclude_attributes.collect!{ |v| v.to_s }
119
- # Attributes we only want to inherit
120
- only_attributes = []
121
- only_attributes << options[:only] if options[:only]
122
- only_attributes.flatten!
123
- only_attributes.collect!{ |v| v.to_s }
110
+ def include_attribute?(attribute_name, options)
111
+ # Attributes we don't want to inherit
112
+ exclude_attributes = ['id']
113
+ exclude_attributes << ['created_at', 'updated_at'] unless options[:include_timestamps]
114
+ exclude_attributes << options[:exclude] if options[:exclude]
115
+ exclude_attributes.flatten!
116
+ exclude_attributes.collect!{ |v| v.to_s }
117
+ # Attributes we only want to inherit
118
+ only_attributes = []
119
+ only_attributes << options[:only] if options[:only]
120
+ only_attributes.flatten!
121
+ only_attributes.collect!{ |v| v.to_s }
124
122
 
125
- !exclude_attributes.include?(attribute_name) && (only_attributes.empty? || only_attributes.include?(attribute_name))
126
- end
123
+ !exclude_attributes.include?(attribute_name) && (only_attributes.empty? || only_attributes.include?(attribute_name))
124
+ end
127
125
 
128
- def skip_validators?(attribute_name, options)
129
- return true if options[:skip_validators] == true
130
- skip_validators = []
131
- skip_validators << options[:skip_validators]
132
- skip_validators.flatten!
133
- skip_validators.collect!{ |v| v.to_s }
134
- skip_validators.include?(attribute_name)
135
- end
136
- end
126
+ def skip_validators?(attribute_name, options)
127
+ return true if options[:skip_validators] == true
128
+ skip_validators = []
129
+ skip_validators << options[:skip_validators]
130
+ skip_validators.flatten!
131
+ skip_validators.collect!{ |v| v.to_s }
132
+ skip_validators.include?(attribute_name)
137
133
  end
138
134
  end
139
135
  end
@@ -1,76 +1,37 @@
1
1
  module Soulless
2
- module Model
3
- def self.included(base)
4
- base.class_eval do
5
- extend ActiveModel::Naming
6
- extend ActiveModel::Translation
7
- extend ActiveModel::Callbacks
8
- include ActiveModel::Conversion
9
- include ActiveModel::Dirty
10
- include ActiveModel::Validations
2
+ class Model
3
+ extend ActiveModel::Callbacks
4
+ extend ActiveModel::Naming
5
+ extend ActiveModel::Translation
11
6
 
12
- class << self
13
- def i18n_scope
14
- :soulless
15
- end
16
- end
7
+ extend Soulless::Inheritance
17
8
 
18
- define_model_callbacks :validation, :save
9
+ include ActiveModel::AttributeMethods
10
+ include ActiveModel::Dirty
11
+ include ActiveModel::SecurePassword
12
+ include ActiveModel::Serialization
13
+ include ActiveModel::Validations
19
14
 
20
- def initialize(params = {})
21
- init_accessors(attribute_set.map { |a| a.name })
22
- super
23
- init_dirty
24
- end
15
+ include Soulless::Dirty
16
+ include Soulless::Validations
25
17
 
26
- def persisted?
27
- false
28
- end
18
+ include Virtus.model
29
19
 
30
- def valid_with_callbacks?
31
- run_callbacks :validation do
32
- valid_without_callbacks?
33
- end
34
- end
20
+ prepend Soulless::Attributes
21
+ prepend Soulless::Callbacks
35
22
 
36
- alias_method_chain :valid?, :callbacks
23
+ def initialize(params = {})
24
+ super
37
25
 
38
- def save
39
- if valid?
40
- run_callbacks :save do
41
- persist!
42
- changes_applied
43
- true
44
- end
45
- else
46
- false
47
- end
48
- end
49
-
50
- def assign_attributes(attributes)
51
- deep_update(self, attributes)
52
- end
53
-
54
- def update_attributes(attributes)
55
- assign_attributes(attributes)
56
- save
57
- end
26
+ apply_changes
27
+ end
58
28
 
59
- private
60
- def persist!
61
- raise 'Method persist! not defined...'
62
- end
29
+ def i18n_scope
30
+ :soulless
31
+ end
63
32
 
64
- def deep_update(object, attributes)
65
- attributes.each do |key, value|
66
- if value.kind_of?(Hash)
67
- deep_update(object.send(key), value)
68
- else
69
- object.send("#{key}=", value)
70
- end
71
- end
72
- end
73
- end
33
+ def persisted?
34
+ false
74
35
  end
75
36
  end
76
37
  end
@@ -5,34 +5,33 @@ module Soulless
5
5
  raise 'ActiveRecord is not defined. The Soulless uniqueness validator cannot be used when ActiveRecord is not present.' unless Object.const_defined?('ActiveRecord')
6
6
  @model = options[:model]
7
7
  @attribute = options[:attribute]
8
- options.merge!(class: @model) if ActiveModel::VERSION::STRING >= '4.1.0'
8
+ options.merge!(class: @model)
9
9
  @validator = ActiveRecord::Validations::UniquenessValidator.new(options)
10
10
  super(options)
11
11
  end
12
-
12
+
13
13
  def validate_each(record, attribute, value)
14
14
  if !@model
15
15
  raise ArgumentError, 'Missing required argument "model"'
16
16
  else
17
17
  record_orig, attribute_orig = record, attribute
18
-
18
+
19
19
  attribute = @attribute if @attribute
20
20
  record = @model.new(attribute => value)
21
-
22
- @validator.setup(@model) if ActiveModel::VERSION::STRING < '4.1'
21
+
23
22
  @validator.validate_each(record, attribute, value)
24
-
23
+
25
24
  if record.errors.any?
26
25
  record_orig.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(value: value))
27
26
  end
28
27
  end
29
28
  end
30
29
  end
31
-
30
+
32
31
  module ClassMethods
33
32
  def validates_uniqueness_of(*attr_name)
34
33
  validates_with(UniquenessValidator, _merge_attributes(attr_name))
35
34
  end
36
35
  end
37
36
  end
38
- end
37
+ end
@@ -1,4 +1,3 @@
1
- require 'soulless/validations/associated_validator'
2
1
  require 'soulless/validations/uniqueness_validator'
3
2
 
4
3
  module Soulless
@@ -7,4 +6,4 @@ module Soulless
7
6
  base.send(:extend, ClassMethods)
8
7
  end
9
8
  end
10
- end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Soulless
2
- VERSION = "0.6.0"
2
+ VERSION = '1.0.0'
3
3
  end
data/lib/soulless.rb CHANGED
@@ -1,32 +1,14 @@
1
- require 'virtus'
2
- require 'securerandom'
3
- require 'active_support'
4
1
  require 'active_model'
2
+ require 'virtus'
5
3
 
6
- require 'soulless/accessors'
7
- require 'soulless/associations'
4
+ require 'soulless/attributes'
5
+ require 'soulless/callbacks'
8
6
  require 'soulless/dirty'
9
7
  require 'soulless/inheritance'
10
- require 'soulless/model'
11
8
  require 'soulless/validations'
9
+ require 'soulless/model'
12
10
  require 'soulless/version'
13
11
 
14
- require 'soulless/railtie' if defined?(Rails)
15
-
16
12
  module Soulless
17
- I18n.load_path += Dir.glob('lib/soulless/locale/*.{rb,yml}')
18
-
19
- def self.model(options = {})
20
- mod = Module.new
21
- mod.define_singleton_method :included do |object|
22
- object.send(:include, Virtus.model(options))
23
- object.send(:include, Model)
24
- object.send(:include, Associations)
25
- object.send(:include, Validations)
26
- object.send(:include, Dirty)
27
- object.send(:include, Accessors)
28
- object.send(:include, Inheritance)
29
- end
30
- mod
31
- end
13
+ # Your code goes here...
32
14
  end
data/soulless.gemspec CHANGED
@@ -4,26 +4,25 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'soulless/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = 'soulless'
7
+ spec.name = "soulless"
8
8
  spec.version = Soulless::VERSION
9
9
  spec.authors = ['Anthony Smith']
10
10
  spec.email = ['anthony@sticksnleaves.com']
11
- spec.description = %q{Rails models without a database (and Rails).}
12
- spec.summary = %q{Create Rails style models without the database.}
13
- spec.homepage = ''
11
+
12
+ spec.summary = %q{Rails models without the database (and Rails)}
13
+ spec.description = %q{Create Rails style models without the database (and Rails).}
14
+ spec.homepage = 'https://github.com/anthonator/soulless'
14
15
  spec.license = 'MIT'
15
16
 
16
- spec.files = `git ls-files`.split($/)
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ['lib']
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
20
21
 
21
- spec.add_runtime_dependency 'activesupport', '>= 3.2'
22
- spec.add_runtime_dependency 'activemodel', '>= 3.2'
23
- spec.add_runtime_dependency 'virtus', '>= 1.0'
22
+ spec.add_dependency 'activemodel', '>= 4.2.0', '< 5.1'
23
+ spec.add_dependency 'virtus' , '~> 1.0.0'
24
24
 
25
25
  spec.add_development_dependency 'bundler'
26
- spec.add_development_dependency 'database_cleaner'
27
26
  spec.add_development_dependency 'rake'
28
- spec.add_development_dependency 'rspec'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
29
28
  end