aduki 0.0.1

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.
@@ -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