aduki 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .#*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in azuki.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 conanite
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,80 @@
1
+ # Aduki
2
+
3
+ Aduki is an attribute setter inspired only a little bit by the "Bean" concept in Java. Here's the idea:
4
+
5
+ props = {
6
+ "name" => "Wilbur",
7
+ "size" => "3",
8
+ "gadgets[0].id" => "FHB5S",
9
+ "gadgets[0].position.x" => "0.45",
10
+ "gadgets[0].position.y" => "0.97",
11
+ "gadgets[1].id" => "SVE21",
12
+ "gadgets[1].position.x" => "0.31",
13
+ "gadgets[1].position.y" => "0.34",
14
+ }
15
+
16
+ model = Model.new props
17
+
18
+ At this point, #model is an instance of Model with its #name, and #size properties initialized as you would expect, and its #gadgets is an array of length 2, with each object initialized as you would expect.
19
+
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ gem 'aduki'
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install aduki
34
+
35
+ ## Usage
36
+
37
+ For a complete example, please see the spec. It's a single file.
38
+
39
+ Here's an abbreviated version:
40
+
41
+ class Assembly
42
+ include Aduki::Initializer
43
+ attr_accessor :name, :colour, :size
44
+ end
45
+
46
+ class Machine
47
+ include Aduki::Initializer
48
+ attr_accessor :name, :weight, :speed, :builder, :team
49
+ attr_accessor :assemblies, :dimensions
50
+ aduki :assemblies => Assembly, :builder => MachineBuilder
51
+ end
52
+
53
+ class Model
54
+ include Aduki::Initializer
55
+
56
+ attr_accessor :name, :email, :item, :thing, :gadget
57
+ attr_accessor :machines, :contraptions, :countries
58
+ aduki :gadget => Gadget, :machines => Machine, :contraptions => Contraption
59
+ end
60
+
61
+
62
+ Aduki::Initializer adds an #initialize instance method that knows how to read a hash of properties and apply them, much like ActiveRecord::Base,
63
+ with one major difference: Aduki splits property names on "." and applies the hash recursively. Aduki also pays special attention to "[]" in property
64
+ names, initializing an array of items instead of an item directly. Aduki uses the array index inside "[]" to distinguish elements, but not to order them.
65
+
66
+ The #aduki class method allows you identify which types to initialise for each field. When a type is not specified for a field, aduki sets the given value directly.
67
+
68
+ aduki :gadget => Gadget, :machines => Machine, :contraptions => Contraption
69
+
70
+ This line instructs aduki to initialise the #gadget field with a Gadget object. It also instructs aduki to initialize each element of the #machines
71
+ array with a Machine object. Aduki decides to create an object or an array of objects depending on the attributes-hash contents, rather than on any
72
+ metadata you may specify here.
73
+
74
+ ## Contributing
75
+
76
+ 1. Fork it
77
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
78
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
79
+ 4. Push to the branch (`git push origin my-new-feature`)
80
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8; mode: ruby -*-
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'aduki/version'
7
+
8
+ Gem::Specification.new do |gem|
9
+ gem.name = "aduki"
10
+ gem.version = Aduki::VERSION
11
+ gem.authors = ["Conan Dalton"]
12
+ gem.email = ["conan@conandalton.net"]
13
+ gem.description = %q{recursive attribute setting for ruby objects}
14
+ gem.summary = %q{set object attributes recursively from an attributes hash}
15
+ gem.homepage = "https://github.com/conanite/aduki"
16
+
17
+ gem.add_development_dependency 'rspec', '~> 2.9'
18
+
19
+ gem.files = `git ls-files`.split($/)
20
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
+ gem.require_paths = ["lib"]
23
+ end
@@ -0,0 +1,62 @@
1
+ require "aduki/version"
2
+
3
+ module Aduki
4
+ def self.to_value klass, setter, value
5
+ type = klass.aduki_type_for_attribute_name setter
6
+ type ? type.new(value) : value
7
+ end
8
+
9
+ def self.apply_attributes object, attrs
10
+ setters = { }
11
+ klass = object.class
12
+
13
+ attrs.each do |setter, value|
14
+ if setter.match /\./
15
+ first, rest = setter.split(/\./, 2)
16
+ setters[first] ||= { }
17
+ setters[first][rest] = value
18
+ elsif setter.match /\[\d+\]/
19
+ setters[setter] = value
20
+ else
21
+ object.send "#{setter}=", value
22
+ end
23
+ end
24
+
25
+ setters.each do |setter, value|
26
+ if setter.match /\[\d+\]/
27
+ setter = setter.gsub /\[\d+\]/, ''
28
+ array = object.send setter.to_sym
29
+ if array == nil
30
+ array = []
31
+ object.send("#{setter}=", array)
32
+ end
33
+ array << to_value(klass, setter, value)
34
+ else
35
+ type = klass.aduki_type_for_attribute_name setter
36
+ object.send "#{setter}=", to_value(klass, setter, value)
37
+ end
38
+ end
39
+ end
40
+
41
+ module ClassMethods
42
+ @@types = { }
43
+
44
+ def aduki types
45
+ @@types[self] = types
46
+ end
47
+
48
+ def aduki_type_for_attribute_name name
49
+ @@types[self][name.to_sym]
50
+ end
51
+ end
52
+
53
+ module Initializer
54
+ def initialize attrs
55
+ Aduki.apply_attributes self, attrs
56
+ end
57
+
58
+ def self.included(base)
59
+ base.extend Aduki::ClassMethods
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ module Aduki
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,142 @@
1
+ require "aduki"
2
+ require "spec_helper"
3
+
4
+ describe Aduki::Initializer do
5
+
6
+ it "should set a bunch of properties from a hash of property paths" do
7
+ props = {
8
+ "name" => "Willy",
9
+ "email" => "willy@wonka.softify.com",
10
+ "item" => "HXB5H",
11
+ "thing" => "0",
12
+ "gadget.name" => "My Big Gadget",
13
+ "gadget.price" => "21",
14
+ "gadget.supplier" => "Apple, Inc.",
15
+ "machines[0].name" => "The First Machine",
16
+ "machines[0].weight" => "88",
17
+ "machines[0].speed" => "142",
18
+ "machines[0].builder.name" => "The First Machine Builder",
19
+ "machines[0].builder.email" => "wibble@bump",
20
+ "machines[0].builder.phone" => "4099",
21
+ "machines[0].builder.office" => "2nd floor room 12",
22
+ "machines[0].assemblies[0].name" => "first machine, first assembly", # the array index distinguishes items but does not order them
23
+ "machines[0].assemblies[1].name" => "first machine, second assembly",
24
+ "machines[0].assemblies[2].name" => "first machine, third assembly",
25
+ "machines[0].assemblies[0].colour" => "red",
26
+ "machines[0].assemblies[1].colour" => "green",
27
+ "machines[0].assemblies[2].colour" => "blue",
28
+ "machines[0].assemblies[0].size" => "pretty large",
29
+ "machines[0].assemblies[1].size" => "sort of medium",
30
+ "machines[0].assemblies[2].size" => "miniscule",
31
+ "machines[0].dimensions[0]" => "2346.56",
32
+ "machines[0].dimensions[1]" => "6456.64",
33
+ "machines[0].dimensions[2]" => "3859.39",
34
+ "machines[0].dimensions[3]" => "2365.68",
35
+ "machines[0].team.lead" => "Shakespeare", # there is no class definition for #team, so Aduki will apply a hash with properties #lead, #code, #design
36
+ "machines[0].team.code" => "Chaucer",
37
+ "machines[0].team.design" => "Jobs",
38
+ "machines[1].name" => "The Second Machine",
39
+ "machines[1].weight" => "34",
40
+ "machines[1].speed" => "289",
41
+ "machines[1].builder.name" => "The Second Machine Builder",
42
+ "machines[1].builder.email" => "waggie@bump",
43
+ "machines[1].builder.phone" => "4101",
44
+ "machines[1].builder.office" => "3rd floor room 23",
45
+ "machines[1].assemblies[98].name" => "second machine, first assembly", # the array index distinguishes items but does not order them
46
+ "machines[1].assemblies[98].colour" => "purple",
47
+ "machines[1].assemblies[98].size" => "pretty small",
48
+ "machines[1].assemblies[1].name" => "second machine, second assembly",
49
+ "machines[1].assemblies[1].colour" => "turquoise",
50
+ "machines[1].assemblies[1].size" => "large-ish",
51
+ "machines[1].assemblies[99].name" => "second machine, third assembly",
52
+ "machines[1].assemblies[99].colour" => "magenta",
53
+ "machines[1].assemblies[99].size" => "gigantic",
54
+ "machines[1].dimensions[20]" => "1985.85",
55
+ "machines[1].dimensions[30]" => "7234.92",
56
+ "machines[1].dimensions[40]" => "9725.52",
57
+ "machines[1].dimensions[50]" => "3579.79",
58
+ "machines[1].team.lead" => "Joyce",
59
+ "machines[1].team.code" => "O'Brien",
60
+ "machines[1].team.design" => "Moore",
61
+ "machines[1].team.muffins" => "MacNamara",
62
+ "contraptions[0].x" => "19",
63
+ "contraptions[0].y" => "0.003",
64
+ "contraptions[1].x" => "12",
65
+ "contraptions[1].y" => "0.0012",
66
+ "contraptions[2].x" => "24",
67
+ "contraptions[2].y" => "0.00063",
68
+ "contraptions[3].x" => "16",
69
+ "contraptions[3].y" => "0.00091",
70
+ "countries[0]" => "France",
71
+ "countries[1]" => "Sweden",
72
+ "countries[2]" => "Germany",
73
+ "countries[3]" => "Ireland",
74
+ "countries[4]" => "Spain",
75
+ }
76
+
77
+ model = Model.new props
78
+
79
+ model.name.should == "Willy"
80
+ model.email.should == "willy@wonka.softify.com"
81
+ model.item.should == "HXB5H"
82
+ model.thing.should == "0"
83
+ model.gadget.name.should == "My Big Gadget"
84
+ model.gadget.price.should == "21"
85
+ model.gadget.supplier.should == "Apple, Inc."
86
+ model.machines[0].name.should == "The First Machine"
87
+ model.machines[0].weight.should == "88"
88
+ model.machines[0].speed.should == "142"
89
+ model.machines[0].builder.name.should == "The First Machine Builder"
90
+ model.machines[0].builder.email.should == "wibble@bump"
91
+ model.machines[0].builder.phone.should == "4099"
92
+ model.machines[0].builder.office.should == "2nd floor room 12"
93
+ model.machines[0].assemblies[0].name.should == "first machine, first assembly"
94
+ model.machines[0].assemblies[0].colour.should == "red"
95
+ model.machines[0].assemblies[0].size.should == "pretty large"
96
+ model.machines[0].assemblies[1].name.should == "first machine, second assembly"
97
+ model.machines[0].assemblies[1].colour.should == "green"
98
+ model.machines[0].assemblies[1].size.should == "sort of medium"
99
+ model.machines[0].assemblies[2].name.should == "first machine, third assembly"
100
+ model.machines[0].assemblies[2].colour.should == "blue"
101
+ model.machines[0].assemblies[2].size.should == "miniscule"
102
+ model.machines[0].dimensions[0].should == "2346.56"
103
+ model.machines[0].dimensions[1].should == "6456.64"
104
+ model.machines[0].dimensions[2].should == "3859.39"
105
+ model.machines[0].dimensions[3].should == "2365.68"
106
+ model.machines[0].team.should == { "lead" => "Shakespeare", "code" => "Chaucer", "design" => "Jobs"}
107
+ model.machines[1].name.should == "The Second Machine"
108
+ model.machines[1].weight.should == "34"
109
+ model.machines[1].speed.should == "289"
110
+ model.machines[1].builder.name.should == "The Second Machine Builder"
111
+ model.machines[1].builder.email.should == "waggie@bump"
112
+ model.machines[1].builder.phone.should == "4101"
113
+ model.machines[1].builder.office.should == "3rd floor room 23"
114
+ model.machines[1].assemblies[0].name.should == "second machine, first assembly"
115
+ model.machines[1].assemblies[0].colour.should == "purple"
116
+ model.machines[1].assemblies[0].size.should == "pretty small"
117
+ model.machines[1].assemblies[1].name.should == "second machine, second assembly"
118
+ model.machines[1].assemblies[1].colour.should == "turquoise"
119
+ model.machines[1].assemblies[1].size.should == "large-ish"
120
+ model.machines[1].assemblies[2].name.should == "second machine, third assembly"
121
+ model.machines[1].assemblies[2].colour.should == "magenta"
122
+ model.machines[1].assemblies[2].size.should == "gigantic"
123
+ model.machines[1].dimensions[0].should == "1985.85"
124
+ model.machines[1].dimensions[1].should == "7234.92"
125
+ model.machines[1].dimensions[2].should == "9725.52"
126
+ model.machines[1].dimensions[3].should == "3579.79"
127
+ model.machines[1].team.should == { "lead" => "Joyce", "code" => "O'Brien", "design" => "Moore", "muffins" => "MacNamara"}
128
+ model.contraptions[0].x.should == "19"
129
+ model.contraptions[0].y.should == "0.003"
130
+ model.contraptions[1].x.should == "12"
131
+ model.contraptions[1].y.should == "0.0012"
132
+ model.contraptions[2].x.should == "24"
133
+ model.contraptions[2].y.should == "0.00063"
134
+ model.contraptions[3].x.should == "16"
135
+ model.contraptions[3].y.should == "0.00091"
136
+ model.countries[0].should == "France"
137
+ model.countries[1].should == "Sweden"
138
+ model.countries[2].should == "Germany"
139
+ model.countries[3].should == "Ireland"
140
+ model.countries[4].should == "Spain"
141
+ end
142
+ end
File without changes
@@ -0,0 +1,42 @@
1
+
2
+ RSpec.configure do |config|
3
+ config.treat_symbols_as_metadata_keys_with_true_values = true
4
+ config.run_all_when_everything_filtered = true
5
+ config.order = 'random'
6
+ end
7
+
8
+ class Contraption
9
+ include Aduki::Initializer
10
+ attr_accessor :x, :y
11
+ end
12
+
13
+ class Assembly
14
+ include Aduki::Initializer
15
+ attr_accessor :name, :colour, :size
16
+ end
17
+
18
+ class MachineBuilder
19
+ include Aduki::Initializer
20
+ attr_accessor :name, :email, :phone, :office
21
+ end
22
+
23
+ class Gadget
24
+ include Aduki::Initializer
25
+ attr_accessor :name, :price, :supplier
26
+ end
27
+
28
+ class Machine
29
+ include Aduki::Initializer
30
+ attr_accessor :name, :weight, :speed, :builder, :team
31
+ attr_accessor :assemblies, :dimensions
32
+ aduki :assemblies => Assembly, :builder => MachineBuilder
33
+ end
34
+
35
+ class Model
36
+ include Aduki::Initializer
37
+
38
+ attr_accessor :name, :email, :item, :thing, :gadget
39
+ attr_accessor :machines, :contraptions, :countries
40
+ aduki :gadget => Gadget, :machines => Machine, :contraptions => Contraption
41
+ end
42
+
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aduki
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Conan Dalton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.9'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.9'
30
+ description: recursive attribute setting for ruby objects
31
+ email:
32
+ - conan@conandalton.net
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - .rspec
39
+ - Gemfile
40
+ - LICENSE.txt
41
+ - README.md
42
+ - Rakefile
43
+ - aduki.gemspec
44
+ - lib/aduki.rb
45
+ - lib/aduki/version.rb
46
+ - spec/initializer_spec.rb
47
+ - spec/model.rb
48
+ - spec/spec_helper.rb
49
+ homepage: https://github.com/conanite/aduki
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.24
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: set object attributes recursively from an attributes hash
73
+ test_files:
74
+ - spec/initializer_spec.rb
75
+ - spec/model.rb
76
+ - spec/spec_helper.rb