nashie 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/README.md +43 -8
- data/lib/nash/version.rb +1 -1
- data/lib/nashie.rb +54 -20
- data/nashie.gemspec +1 -1
- data/spec/nashie_spec.rb +92 -0
- metadata +64 -68
- data/spec/nash_spec.rb +0 -49
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Nashie
|
2
|
-
|
3
|
-
|
2
|
+
|
3
|
+
Nashie takes the Hashie concept one step further and allows you to create nested structure with buildin validation logic.
|
4
|
+
|
5
|
+
Nashies are used in the wild as presenters for API responses and to validate incoming API calls.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -18,16 +20,49 @@ Or install it yourself as:
|
|
18
20
|
|
19
21
|
## Usage
|
20
22
|
|
23
|
+
class Part < Hashie::Nash
|
24
|
+
property :name, :required => true
|
25
|
+
end
|
26
|
+
|
27
|
+
class Engine < Hashie::Nash
|
28
|
+
property :build_year, :required => true
|
29
|
+
property :nitro, :default => false
|
30
|
+
property :parts, :collection => true, :class => "Part"
|
31
|
+
end
|
32
|
+
|
21
33
|
class Car < Hashie::Nash
|
22
|
-
class Engine
|
23
|
-
|
24
|
-
|
34
|
+
property :engine, :required => true, :class => Engine
|
35
|
+
end
|
36
|
+
|
37
|
+
car = Car.new "engine" => {"build_year" => 2010, "nitro" => false, :parts => [{"name" => "crankshaft"}, {"name" => "flywheel"}]}
|
38
|
+
## Overrides
|
39
|
+
|
40
|
+
It's possible that you have custom validation logic that is not covered by nashies.
|
41
|
+
Internally nashies use the "new" class method which can be overridden if needed.
|
42
|
+
|
43
|
+
class Motorcycle
|
44
|
+
def self.new hash
|
45
|
+
raise "Euhm, a motorcycle has 2 wheels you know..." if not hash[:wheels].eql? 2
|
46
|
+
super hash
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
## DSL
|
51
|
+
|
52
|
+
A DSL is created that acts similar, it works by autocreating the classes as illustrated above (namespaced to the parent class).
|
53
|
+
The example below for example will convert the block to a Presentation::Users class with similar properties.
|
54
|
+
The block body passed to the property is executed on the class-level so you can define methods and override methods if needed.
|
55
|
+
|
56
|
+
class Presentation < Hashie::Nash
|
57
|
+
property :users, :collection => true, :required => true do
|
58
|
+
property :name
|
25
59
|
end
|
26
|
-
|
27
|
-
property :engine, :required => true, :class => Engine
|
28
60
|
end
|
29
61
|
|
30
|
-
|
62
|
+
## Future work
|
63
|
+
|
64
|
+
- more types of validations
|
65
|
+
- Customizable error messages
|
31
66
|
|
32
67
|
## Contributing
|
33
68
|
|
data/lib/nash/version.rb
CHANGED
data/lib/nashie.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require "nash/version"
|
2
|
-
require 'hashie/
|
2
|
+
require 'hashie/trash'
|
3
3
|
require 'set'
|
4
4
|
|
5
5
|
module Hashie
|
6
6
|
# A Nash is a Nested Defined Hash and is an extension of a Dash
|
7
|
-
# A Nash allows you to create complex assignments composing of
|
7
|
+
# A Nash allows you to create complex assignments composing of
|
8
8
|
# several nested Dashes
|
9
|
-
#
|
10
9
|
#
|
11
|
-
#
|
10
|
+
#
|
11
|
+
# Nashes are useful when you need to create a lightweigth nested
|
12
12
|
# data object, ideal for validating JSON
|
13
|
-
class Nash < Hashie::
|
13
|
+
class Nash < Hashie::Trash
|
14
14
|
include Hashie::PrettyInspect
|
15
15
|
alias_method :to_s, :inspect
|
16
16
|
|
@@ -34,7 +34,14 @@ module Hashie
|
|
34
34
|
# should be instantiated when an assignment is made to this
|
35
35
|
# property.
|
36
36
|
#
|
37
|
-
def self.property(property_name, options = {})
|
37
|
+
def self.property(property_name, options = {}, &block)
|
38
|
+
if block_given?
|
39
|
+
# if my college professor sees this he will personally come and beat the shit out of me :(
|
40
|
+
class_name = property_name.to_s.capitalize
|
41
|
+
class_ref = const_set(class_name, Class.new(Hashie::Nash))
|
42
|
+
class_ref.class_eval &block
|
43
|
+
options[:class] = class_name
|
44
|
+
end
|
38
45
|
property_name = property_name.to_sym
|
39
46
|
|
40
47
|
self.properties << property_name
|
@@ -42,18 +49,45 @@ module Hashie
|
|
42
49
|
if options.has_key?(:class)
|
43
50
|
class_name = options[:class].to_s
|
44
51
|
class_properties[property_name] = class_name if options.delete(:class)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
52
|
+
if not options[:collection]
|
53
|
+
class_eval <<-ACCESSORS
|
54
|
+
def #{property_name}(&block)
|
55
|
+
self.[](#{property_name.to_s.inspect}, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def #{property_name}=(value)
|
59
|
+
self.[]=(#{property_name.to_s.inspect}, #{class_properties[property_name]}.new(value))
|
60
|
+
end
|
61
|
+
|
62
|
+
ACCESSORS
|
63
|
+
else
|
64
|
+
# expect arrays and convert single items into arrays
|
65
|
+
class_eval <<-ACCESSORS
|
66
|
+
def #{property_name}(&block)
|
67
|
+
self.[](#{property_name.to_s.inspect}, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def #{property_name}=(value)
|
71
|
+
if not value.is_a? Array
|
72
|
+
if #{options[:permissive]}
|
73
|
+
value = [value]
|
74
|
+
else
|
75
|
+
raise "#{property_name} requires an array, use :permissive => true to automatically convert to array"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
values = []
|
79
|
+
value.each do |item|
|
80
|
+
values << #{class_properties[property_name]}.new(item)
|
81
|
+
end
|
82
|
+
values =
|
83
|
+
self.[]=(#{property_name.to_s.inspect}, values)
|
84
|
+
end
|
85
|
+
|
86
|
+
ACCESSORS
|
87
|
+
|
88
|
+
end
|
89
|
+
|
49
90
|
|
50
|
-
def #{property_name}=(value)
|
51
|
-
self.[]=(#{property_name.to_s.inspect}, #{class_properties[property_name]}.new(value))
|
52
|
-
end
|
53
|
-
|
54
|
-
ACCESSORS
|
55
|
-
|
56
|
-
|
57
91
|
elsif
|
58
92
|
class_properties[property_name] = nil
|
59
93
|
end
|
@@ -97,11 +131,11 @@ module Hashie
|
|
97
131
|
# You may initialize a Dash with an attributes hash
|
98
132
|
# just like you would many other kinds of data objects.
|
99
133
|
def initialize(attributes = {}, &block)
|
100
|
-
super(attributes, &block)
|
101
|
-
|
134
|
+
super(attributes, &block)
|
135
|
+
|
102
136
|
# override whatever Dash has set
|
103
137
|
attributes.each_pair do |att, value|
|
104
|
-
self.send((att.to_s + '=').to_sym,value)
|
138
|
+
self.send((att.to_s + '=').to_sym,value)
|
105
139
|
end if attributes
|
106
140
|
assert_required_properties_set!
|
107
141
|
end
|
data/nashie.gemspec
CHANGED
@@ -3,7 +3,7 @@ require File.expand_path('../lib/nash/version', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["noverloop"]
|
6
|
-
gem.email = ["nicolas@
|
6
|
+
gem.email = ["nicolas@noverloop.be"]
|
7
7
|
gem.description = %q{Nashie is an extension of Hashie and stands for Nested Hashie. Nashie allows you to create hashes that include other objects}
|
8
8
|
gem.summary = %q{Nashie allows you to create nested Hashie structures}
|
9
9
|
gem.homepage = ""
|
data/spec/nashie_spec.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hashie::Nash do
|
4
|
+
|
5
|
+
class Test1 < Hashie::Nash
|
6
|
+
property :meh,:required => true
|
7
|
+
end
|
8
|
+
|
9
|
+
class NestedTest < Hashie::Nash
|
10
|
+
property :nest, :class => Test1
|
11
|
+
end
|
12
|
+
|
13
|
+
class NestedWithStringTest < Hashie::Nash
|
14
|
+
property :nest, :class => "Test1"
|
15
|
+
end
|
16
|
+
|
17
|
+
class Phrase < Hashie::Nash
|
18
|
+
property :phrase, :required => true
|
19
|
+
end
|
20
|
+
|
21
|
+
class NestedList < Hashie::Nash
|
22
|
+
property :nests, :class => "Phrase", :collection => true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should create a new Test1" do
|
26
|
+
t = Test1.new "meh"=> 5
|
27
|
+
t.nil?.should == false
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should create a new NestedTest" do
|
31
|
+
t = NestedTest.new "nest" => {"meh"=> 5}
|
32
|
+
t.nil?.should == false
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should make nested hashes accessible" do
|
36
|
+
t = NestedTest.new "nest" => {"meh"=> 5}
|
37
|
+
t.nest.meh.should == 5
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should make nested hashes with strings accessible" do
|
41
|
+
t = NestedWithStringTest.new "nest" => {"meh"=> 5}
|
42
|
+
t.nest.meh.should == 5
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should deal with collections of items properly" do
|
46
|
+
t = NestedList.new "nests" => [{"phrase" => "phrase 1"}, {"phrase" => "phrase 2"}]
|
47
|
+
t.nests.count.should == 2
|
48
|
+
t.nests.first.phrase.should == "phrase 1"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should make validate required properties in nested dash" do
|
52
|
+
lambda do
|
53
|
+
t = NestedTest.new "nest" => {"non_existent"=> 5}
|
54
|
+
t.nest.meh.should == 5
|
55
|
+
end.should raise_error NoMethodError
|
56
|
+
end
|
57
|
+
|
58
|
+
class Part < Hashie::Nash
|
59
|
+
property :name, :required => true
|
60
|
+
end
|
61
|
+
|
62
|
+
class Engine < Hashie::Nash
|
63
|
+
property :build_year, :required => true
|
64
|
+
property :nitro, :default => false
|
65
|
+
property :parts, :collection => true, :class => "Part"
|
66
|
+
end
|
67
|
+
|
68
|
+
class Car < Hashie::Nash
|
69
|
+
property :engine, :required => true, :class => Engine
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
it "should make validate required properties in nested dash" do
|
74
|
+
car = Car.new "engine" => {"build_year" => 2010, "nitro" => false, :parts => [{"name" => "crankshaft"}, {"name" => "flywheel"}]}
|
75
|
+
car.engine.build_year.should == 2010
|
76
|
+
car.engine.nitro.should be_false
|
77
|
+
car.engine.parts.count.should == 2
|
78
|
+
car.engine.parts.first.name.should == "crankshaft"
|
79
|
+
end
|
80
|
+
|
81
|
+
class Presentation < Hashie::Nash
|
82
|
+
property :users, :collection => true, :required => true do
|
83
|
+
property :name
|
84
|
+
end
|
85
|
+
end
|
86
|
+
require "pry"
|
87
|
+
it "should accept blocks" do
|
88
|
+
p = Presentation.new :users => [{"name"=> "Bob"}, {"name"=>"Eve"}]
|
89
|
+
p.users.count.should == 2
|
90
|
+
p.users.first.name.should == "Bob"
|
91
|
+
end
|
92
|
+
end
|
metadata
CHANGED
@@ -1,87 +1,83 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: nashie
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
4
5
|
prerelease:
|
5
|
-
version: 0.0.3
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
8
|
-
|
7
|
+
authors:
|
8
|
+
- noverloop
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
description: Nashie is an extension of Hashie and stands for Nested Hashie. Nashie
|
38
|
-
|
39
|
-
|
12
|
+
date: 2013-04-12 00:00:00.000000000 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hashie
|
17
|
+
requirement: &2164468960 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2164468960
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake
|
28
|
+
requirement: &2164468400 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2164468400
|
37
|
+
description: Nashie is an extension of Hashie and stands for Nested Hashie. Nashie
|
38
|
+
allows you to create hashes that include other objects
|
39
|
+
email:
|
40
|
+
- nicolas@noverloop.be
|
40
41
|
executables: []
|
41
|
-
|
42
42
|
extensions: []
|
43
|
-
|
44
43
|
extra_rdoc_files: []
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
homepage:
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- .rspec
|
47
|
+
- Gemfile
|
48
|
+
- LICENSE
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- lib/nash/version.rb
|
52
|
+
- lib/nashie.rb
|
53
|
+
- nashie.gemspec
|
54
|
+
- spec/nashie_spec.rb
|
55
|
+
- spec/spec_helper.rb
|
56
|
+
has_rdoc: true
|
57
|
+
homepage: ''
|
59
58
|
licenses: []
|
60
|
-
|
61
59
|
post_install_message:
|
62
60
|
rdoc_options: []
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
64
|
none: false
|
68
|
-
requirements:
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
70
|
none: false
|
74
|
-
requirements:
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
78
75
|
requirements: []
|
79
|
-
|
80
76
|
rubyforge_project:
|
81
|
-
rubygems_version: 1.
|
77
|
+
rubygems_version: 1.5.2
|
82
78
|
signing_key:
|
83
79
|
specification_version: 3
|
84
80
|
summary: Nashie allows you to create nested Hashie structures
|
85
|
-
test_files:
|
86
|
-
|
87
|
-
|
81
|
+
test_files:
|
82
|
+
- spec/nashie_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
data/spec/nash_spec.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Hashie::Nash do
|
4
|
-
|
5
|
-
class Test1 < Hashie::Nash
|
6
|
-
property :meh,:required => true
|
7
|
-
end
|
8
|
-
|
9
|
-
class NestedTest < Hashie::Nash
|
10
|
-
property :nest, :class => Test1
|
11
|
-
end
|
12
|
-
|
13
|
-
it "should create a new Test1" do
|
14
|
-
t = Test1.new "meh"=> 5
|
15
|
-
t.nil?.should == false
|
16
|
-
end
|
17
|
-
|
18
|
-
it "should create a new NestedTest" do
|
19
|
-
t = NestedTest.new "nest" => {"meh"=> 5}
|
20
|
-
t.nil?.should == false
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should make nested hashes accessible" do
|
24
|
-
t = NestedTest.new "nest" => {"meh"=> 5}
|
25
|
-
t.nest.meh.should == 5
|
26
|
-
end
|
27
|
-
|
28
|
-
it "should make validate required properties in nested dash" do
|
29
|
-
lambda do
|
30
|
-
t = NestedTest.new "nest" => {"non_existent"=> 5}
|
31
|
-
t.nest.meh.should == 5
|
32
|
-
end.should raise_error NoMethodError
|
33
|
-
end
|
34
|
-
|
35
|
-
class Car < Hashie::Nash
|
36
|
-
class Engine < Hashie::Nash
|
37
|
-
property :crankshaft, :required => true
|
38
|
-
property :nitro, :default => false
|
39
|
-
end
|
40
|
-
property :engine, :required => true, :class => Engine
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
it "should make validate required properties in nested dash" do
|
45
|
-
c = Car.new "engine" => {"crankshaft" => true, "nitro" => false}
|
46
|
-
c.engine.crankshaft.should == true
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|