modelish 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private --markup markdown lib/**/*.rb - CHANGELOG.md LICENSE
data/CHANGELOG.md CHANGED
@@ -1,9 +1,17 @@
1
- ## O.1.2 (2011-03-31)
1
+ # CHANGELOG
2
+
3
+ ## 0.1.3 (2011-10-25)
4
+
5
+ * Added configuration option to ignore unknown properties. The default behavior continues
6
+ to be raising an error when an unknown property name is encountered in an initialization
7
+ hash.
8
+
9
+ ## 0.1.2 (2011-03-31)
2
10
 
3
11
  * Fixed SystemStackError when a typed property with the same name is defined multiple times.
4
12
  * Use explicit path when loading modelish classes.
5
13
 
6
- ## O.1.1 (2011-03-28)
14
+ ## 0.1.1 (2011-03-28)
7
15
 
8
16
  * Add DateTime to standard supported property types.
9
17
 
data/README.md CHANGED
@@ -3,6 +3,8 @@
3
3
  When a real modeling framework is too heavy, sometimes you want something just
4
4
  a little modelish.
5
5
 
6
+ ## Overview ##
7
+
6
8
  If a Hash or OpenStruct almost suits your needs, but you need bits of
7
9
  model-like behavior such as simple validation and typed values, modelish can
8
10
  help.
@@ -10,18 +12,20 @@ help.
10
12
  If you need persistence or anything but the most basic functionality, modelish
11
13
  will frustrate you to no end.
12
14
 
13
- ## Installation ##
15
+ ## Documentation ##
14
16
 
15
- For the especially foolhardy, you can:
17
+ See the [rdocs][docs].
16
18
 
17
- 1. Add this to your Gemfile:
19
+ ## Installation ##
18
20
 
19
- gem 'modelish', :git => 'git://github.com/maeve/modelish.git'
21
+ modelish is available on [Rubygems][rubygems] and can be installed via:
20
22
 
21
- 2. Execute `bundle install`
23
+ $ gem install modelish
22
24
 
23
25
  ## Basics ##
24
26
 
27
+ ### Property Types ###
28
+
25
29
  The modelish syntax is very similar to some of the classes provided by
26
30
  [hashie]. In fact, the initial implementation simply extended
27
31
  [Hashie::Trash][trash] to add property types:
@@ -44,21 +48,23 @@ key-mappings:
44
48
  :my_simple_property => 'bar')
45
49
 
46
50
  f.my_date
47
- => #<Date: 2011-03-10 (4911261/2,0,2299161)>
51
+ # => #<Date: 2011-03-10 (4911261/2,0,2299161)>
48
52
  f.my_float
49
- => 0.0
53
+ # => 0.0
50
54
  f.my_funky_id
51
- => 42
55
+ # => 42
52
56
  f.my_simple_property
53
- => "bar"
57
+ # => "bar"
58
+
59
+ ### Property Validation ###
54
60
 
55
61
  modelish also supports defining simple property validations:
56
62
 
57
63
  class Bar < Modelish::Base
58
- property :important_field, :required => true
59
- property :state, :max_length => 2
60
- property :my_int, :type => Integer, :validate_type => true
61
- property :another_field, :validator => lambda { |val| "val must respond to []" unless val.respond_to?(:[]) }
64
+ property :important_field, :required => true
65
+ property :state, :max_length => 2
66
+ property :my_int, :type => Integer, :validate_type => true
67
+ property :another_field, :validator => lambda { |val| "val must respond to []" unless val.respond_to?(:[]) }
62
68
  end
63
69
 
64
70
  Validations can be run using methods that return an error map (keyed on property name), raise errors, or return a boolean value to indicate validation outcome.
@@ -68,28 +74,67 @@ Validations can be run using methods that return an error map (keyed on property
68
74
  :my_int => 42,
69
75
  :another_field => Hash.new)
70
76
  valid_bar.valid?
71
- => true
77
+ # => true
72
78
 
73
79
  valid_bar.validate
74
- => {}
80
+ # => {}
75
81
 
76
82
  valid_bar.validate!
77
- => nil
83
+ # => nil
78
84
 
79
85
 
80
86
  invalid_bar = Bar.new(:state => 'a value that is too long',
81
87
  :my_int => 'this is not an integer',
82
88
  :another_field => Object.new)
83
89
  invalid_bar.valid?
84
- => false
90
+ # => false
85
91
 
86
92
  invalid_bar.validate
87
- => {:important_field=>[#<ArgumentError: important_field must not be nil or blank>], :my_int=>[#<ArgumentError: my_int must be of type Integer, but got "this is not an integer">], :another_field=>[#<ArgumentError: val must respond to []>], :state=>[#<ArgumentError: state must be less than 2 characters>]}
93
+ # => {:important_field=>[#<ArgumentError: important_field must not be nil or blank>],
94
+ # :my_int=>[#<ArgumentError: my_int must be of type Integer, but got "this is not an integer">],
95
+ # :another_field=>[#<ArgumentError: val must respond to []>],
96
+ # :state=>[#<ArgumentError: state must be less than 2 characters>]}
88
97
 
89
98
  invalid_bar.validate!
90
- ArgumentError: important_field must not be nil or blank
91
- from /Users/maeverevels/projects/modelish/lib/modelish/validations.rb:31:in `validate!'
92
- ...
99
+ # ArgumentError: important_field must not be nil or blank
100
+
101
+ ## Configuration ##
102
+
103
+ By default, modelish will raise an error when it encounters unknown property names in an initialization hash. If you'd prefer modelish to ignore unknown properties, you can override this default behavior for all of your modelish models:
104
+
105
+ require 'modelish'
106
+
107
+ Modelish.configure do |config|
108
+ config.ignore_unknown_properties = true
109
+ end
110
+
111
+ class MyModel < Modelish::Base
112
+ property :foo
113
+ end
114
+
115
+ m = MyModel.new(:foo => 'value', :bar => true)
116
+ # => <#MyModel foo="value">
117
+
118
+ Or you can selectively enable the setting for a particular model:
119
+
120
+ require 'modelish'
121
+
122
+ class MyPermissiveModel < Modelish::Base
123
+ property :foo
124
+ ignore_unknown_properties!
125
+ end
126
+
127
+ class MyStrictModel < Modelish::Base
128
+ property :foo
129
+ end
130
+
131
+ p = MyPermissiveModel.new(:foo => 'value', :bar => true)
132
+ # => <#MyPermissiveModel foo="value">
133
+
134
+ s = MyStrictModel.new(:foo => 'value', :bar => true)
135
+ # NoMethodError: The property 'bar' is not defined for this Modelish object
93
136
 
94
137
  [hashie]: https://github.com/intridea/hashie
95
138
  [trash]: http://rdoc.info/github/intridea/hashie/master/Hashie/Trash
139
+ [rubygems]: https://rubygems.org/gems/modelish
140
+ [docs]: http://rubydoc.info/gems/modelish
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ task :default => :spec
9
9
  namespace :doc do
10
10
  require 'yard'
11
11
  YARD::Rake::YardocTask.new do |task|
12
- task.files = ['README.md', 'lib/**/*.rb']
12
+ task.files = ['README.md', 'CHANGELOG.md', 'LICENSE', 'lib/**/*.rb']
13
13
  task.options = [
14
14
  '--markup', 'markdown',
15
15
  ]
data/lib/modelish.rb CHANGED
@@ -2,7 +2,8 @@ $:.unshift(File.expand_path('modelish',File.dirname(__FILE__)))
2
2
 
3
3
  require 'modelish/version'
4
4
  require 'modelish/base'
5
+ require 'modelish/configuration'
5
6
 
6
7
  module Modelish
7
- # Your code goes here...
8
+ extend Configuration
8
9
  end
data/lib/modelish/base.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  require 'hashie'
2
- require 'property_types'
3
- require 'validations'
2
+ require 'modelish/property_types'
3
+ require 'modelish/validations'
4
+ require 'modelish/configuration'
4
5
 
5
6
  module Modelish
6
7
  class Base < Hashie::Trash
7
8
  include PropertyTypes
8
9
  include Validations
10
+ extend Configuration
9
11
 
10
12
  # Creates a new attribute.
11
13
  #
@@ -19,14 +21,14 @@ module Modelish
19
21
  # @option opts [Class,Proc] :type the type of the property value. For
20
22
  # a list of accepted types, see
21
23
  # {Modelish::PropertyTypes}
22
- # @options opts [true,false] :required enables validation for the property
24
+ # @option opts [true,false] :required enables validation for the property
23
25
  # value's presence; nil or blank values
24
26
  # will cause validation methods to fail
25
- # @options opts [Integer] :max_length the maximum allowable length for a valid
27
+ # @option opts [Integer] :max_length the maximum allowable length for a valid
26
28
  # property value
27
- # @options opts [true,false] :validate_type enables validation for the property value's
29
+ # @option opts [true,false] :validate_type enables validation for the property value's
28
30
  # type based on the :type option
29
- # @options opts [Proc] :validator A block that accepts a value and validates it;
31
+ # @option opts [Proc] :validator A block that accepts a value and validates it;
30
32
  # should return nil if validation passes, or an error
31
33
  # message or error object if validation fails.
32
34
  # See {Modelish::Validations}
@@ -40,5 +42,17 @@ module Modelish
40
42
  add_validator(name, &options[:validator]) if options[:validator]
41
43
  add_validator(name) { |val| validate_type(name, val, options[:type]) } if options[:validate_type]
42
44
  end
45
+
46
+ private
47
+ def property_exists?(property)
48
+ if self.class.property?(property.to_sym)
49
+ true
50
+ elsif self.class.ignore_unknown_properties ||
51
+ (self.class.ignore_unknown_properties.nil? && Modelish.ignore_unknown_properties)
52
+ false
53
+ else
54
+ raise NoMethodError, "The property '#{property}' is not defined for this Modelish object."
55
+ end
56
+ end
43
57
  end
44
58
  end
@@ -0,0 +1,50 @@
1
+ module Modelish
2
+ module Configuration
3
+ # If true, ignore unknown property names when initializing new models;
4
+ # otherwise, raise an error when an unknown property is encountered.
5
+ # Defaults to false.
6
+ attr_accessor :ignore_unknown_properties
7
+
8
+ # When set, unknown property names will be ignored during modelish initialization
9
+ # and property setting.
10
+ #
11
+ # @see {raise_errors_on_unknown_properties!}
12
+ def ignore_unknown_properties!
13
+ self.ignore_unknown_properties = true
14
+ end
15
+
16
+ # When set, unknown property names will cause errors to be raised when encountered
17
+ # during modelish initialization and property setting. This is the default behavior.
18
+ #
19
+ # @see {ignore_unknown_properties!}
20
+ def raise_errors_on_unknown_properties!
21
+ self.ignore_unknown_properties = false
22
+ end
23
+
24
+ # When this module is extended, set all configuration options to their default values
25
+ def self.extended(base)
26
+ base.reset
27
+ end
28
+
29
+ # Configures this module through the given +block+.
30
+ # Default configuration options will be applied unless
31
+ # they are explicitly overridden in the +block+.
32
+ #
33
+ # @example Disable raising errors on unknown property names
34
+ # Modelish.configure do |config|
35
+ # config.ignore_unknown_properties = true
36
+ # end
37
+ def configure
38
+ if block_given?
39
+ yield self
40
+ end
41
+
42
+ self
43
+ end
44
+
45
+ # Resets this module's configuration.
46
+ def reset
47
+ self.ignore_unknown_properties = false
48
+ end
49
+ end
50
+ end
@@ -1,3 +1,3 @@
1
1
  module Modelish
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
data/modelish.gemspec CHANGED
@@ -24,7 +24,6 @@ Gem::Specification.new do |s|
24
24
  s.add_development_dependency('rspec','~> 2.5')
25
25
  s.add_development_dependency('yard', '~> 0.6')
26
26
  s.add_development_dependency('bluecloth','~> 2.0.11')
27
- s.add_development_dependency('autotest')
28
27
 
29
28
  s.has_rdoc = true
30
29
  end
@@ -10,6 +10,45 @@ describe Modelish::Base do
10
10
 
11
11
  it { should respond_to(:property) }
12
12
 
13
+ context 'without any properties' do
14
+ subject { model }
15
+
16
+ context 'when the model is initialized with an unknown property' do
17
+ let(:init_options) { {unknown_prop => 'whatever'} }
18
+ let(:unknown_prop) { :foo }
19
+
20
+ context 'when ignore_unknown_properties is configured at the class level' do
21
+ before { model_class.ignore_unknown_properties = ignore_unknown_props }
22
+ after { model_class.reset }
23
+
24
+ it_should_behave_like 'an unknown property handler'
25
+ end
26
+
27
+ context "when ignore_unknown_properties is configured globally" do
28
+ before { Modelish.configure { |c| c.ignore_unknown_properties = ignore_unknown_props } }
29
+ after { Modelish.reset }
30
+
31
+ it_should_behave_like 'an unknown property handler'
32
+
33
+ context 'when ignore_unknown_properties is configured differently at the class level' do
34
+ before { Modelish.configure { |c| c.ignore_unknown_properties = !ignore_unknown_props } }
35
+ after { Modelish.reset }
36
+
37
+ before { model_class.ignore_unknown_properties = ignore_unknown_props }
38
+ after { model_class.reset }
39
+
40
+ it_should_behave_like 'an unknown property handler'
41
+ end
42
+ end
43
+
44
+ context 'when ignore_unknown_properties has not been configured' do
45
+ it 'should raise an error' do
46
+ expect { subject }.to raise_error(NoMethodError)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
13
52
  context "with simple property" do
14
53
  before { model_class.property(property_name) }
15
54
 
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe Modelish::Configuration do
4
+ let(:test_module) do
5
+ module TestModule
6
+ extend Modelish::Configuration
7
+ end
8
+ end
9
+
10
+ subject { test_module }
11
+
12
+ after { test_module.reset }
13
+
14
+ it { should respond_to(:configure) }
15
+
16
+ context 'with default configuration' do
17
+ its(:ignore_unknown_properties) { should be_false }
18
+ end
19
+
20
+ describe '.configure' do
21
+ subject { test_module.configure(&config_block) }
22
+
23
+ context 'with full configuration' do
24
+ let(:config_block) do
25
+ lambda do |config|
26
+ config.ignore_unknown_properties = ignore_unknown_props
27
+ end
28
+ end
29
+
30
+ context 'when ignore_unknown_properties is true' do
31
+ let(:ignore_unknown_props) { true }
32
+ its(:ignore_unknown_properties) { should be_true }
33
+ end
34
+
35
+ context 'when ignore_unknown_properties is false' do
36
+ let(:ignore_unknown_props) { false }
37
+ its(:ignore_unknown_properties) { should be_false }
38
+ end
39
+ end
40
+ end
41
+
42
+ describe '.reset' do
43
+ subject { test_module.reset }
44
+
45
+ before { test_module.configure { |c| c.ignore_unknown_properties = true } }
46
+
47
+ it 'should reset the value of ignore_unknown_properties' do
48
+ expect { subject }.to change { test_module.ignore_unknown_properties }.from(true).to(false)
49
+ end
50
+ end
51
+
52
+ describe 'ignore_unknown_properties!' do
53
+ before { test_module.ignore_unknown_properties = ignore_unknown_props }
54
+
55
+ subject { test_module.ignore_unknown_properties! }
56
+
57
+ context 'when ignore_unknown_properties is true' do
58
+ let(:ignore_unknown_props) { true }
59
+
60
+ it 'should not change the setting' do
61
+ expect { subject }.to_not change { test_module.ignore_unknown_properties }
62
+ end
63
+ end
64
+
65
+ context 'when ignore_unknown_properties is false' do
66
+ let(:ignore_unknown_props) { false }
67
+
68
+ it 'should change the setting' do
69
+ expect { subject }.to change { test_module.ignore_unknown_properties }.from(false).to(true)
70
+ end
71
+ end
72
+
73
+ context 'when ignore_unknown_properties is nil' do
74
+ let(:ignore_unknown_props) { nil }
75
+
76
+ it 'should change the setting' do
77
+ expect { subject }.to change { test_module.ignore_unknown_properties }.from(nil).to(true)
78
+ end
79
+ end
80
+ end
81
+
82
+ describe 'raise_errors_on_unknown_properties!' do
83
+ before { test_module.ignore_unknown_properties = ignore_unknown_props }
84
+
85
+ subject { test_module.raise_errors_on_unknown_properties! }
86
+
87
+ context 'when ignore_unknown_properties is true' do
88
+ let(:ignore_unknown_props) { true }
89
+
90
+ it 'should change the setting' do
91
+ expect { subject }.to change { test_module.ignore_unknown_properties }.from(true).to(false)
92
+ end
93
+ end
94
+
95
+ context 'when ignore_unknown_properties is false' do
96
+ let(:ignore_unknown_props) { false }
97
+
98
+ it 'should not change the setting' do
99
+ expect { subject }.to_not change { test_module.ignore_unknown_properties }
100
+ end
101
+ end
102
+
103
+ context 'when ignore_unknown_properties is nil' do
104
+ let(:ignore_unknown_props) { nil }
105
+
106
+ it 'should change the setting' do
107
+ expect { subject }.to change { test_module.ignore_unknown_properties }.from(nil).to(false)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,19 @@
1
+ shared_examples_for 'an unknown property handler' do
2
+ context 'when ignore_unknown_properties is set to false' do
3
+ let(:ignore_unknown_props) { false }
4
+
5
+ it 'should raise an error' do
6
+ expect { subject }.to raise_error(NoMethodError)
7
+ end
8
+ end
9
+
10
+ context 'when ignore_unknown_properties is set to true' do
11
+ let(:ignore_unknown_props) { true }
12
+
13
+ it 'should not raise an error' do
14
+ expect { subject }.to_not raise_error
15
+ end
16
+
17
+ it { should_not respond_to(unknown_prop) }
18
+ end
19
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modelish
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 29
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 2
10
- version: 0.1.2
9
+ - 3
10
+ version: 0.1.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Maeve Revels
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-31 00:00:00 -07:00
18
+ date: 2011-10-25 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -79,20 +79,6 @@ dependencies:
79
79
  version: 2.0.11
80
80
  type: :development
81
81
  version_requirements: *id004
82
- - !ruby/object:Gem::Dependency
83
- name: autotest
84
- prerelease: false
85
- requirement: &id005 !ruby/object:Gem::Requirement
86
- none: false
87
- requirements:
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- hash: 3
91
- segments:
92
- - 0
93
- version: "0"
94
- type: :development
95
- version_requirements: *id005
96
82
  description: Sometimes you need something just a little modelish.
97
83
  email:
98
84
  - maeve.revels@g5platform.com
@@ -106,6 +92,7 @@ files:
106
92
  - .gitignore
107
93
  - .rspec
108
94
  - .rvmrc
95
+ - .yardopts
109
96
  - CHANGELOG.md
110
97
  - Gemfile
111
98
  - LICENSE
@@ -113,17 +100,20 @@ files:
113
100
  - Rakefile
114
101
  - lib/modelish.rb
115
102
  - lib/modelish/base.rb
103
+ - lib/modelish/configuration.rb
116
104
  - lib/modelish/property_types.rb
117
105
  - lib/modelish/validations.rb
118
106
  - lib/modelish/version.rb
119
107
  - modelish.gemspec
120
108
  - spec/modelish/base_spec.rb
109
+ - spec/modelish/configuration_spec.rb
121
110
  - spec/modelish/property_types_spec.rb
122
111
  - spec/modelish/validations_spec.rb
123
112
  - spec/modelish_spec.rb
124
113
  - spec/spec_helper.rb
125
114
  - spec/support/property_examples.rb
126
115
  - spec/support/typed_property_examples.rb
116
+ - spec/support/unknown_property_examples.rb
127
117
  - spec/support/validation_examples.rb
128
118
  has_rdoc: true
129
119
  homepage: http://github.com/maeve/modelish
@@ -161,10 +151,12 @@ specification_version: 3
161
151
  summary: A lightweight pseudo-modeling not-quite-framework
162
152
  test_files:
163
153
  - spec/modelish/base_spec.rb
154
+ - spec/modelish/configuration_spec.rb
164
155
  - spec/modelish/property_types_spec.rb
165
156
  - spec/modelish/validations_spec.rb
166
157
  - spec/modelish_spec.rb
167
158
  - spec/spec_helper.rb
168
159
  - spec/support/property_examples.rb
169
160
  - spec/support/typed_property_examples.rb
161
+ - spec/support/unknown_property_examples.rb
170
162
  - spec/support/validation_examples.rb