embedded_record 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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .*.sw*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in embedded_record.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # EmbeddedRecord
2
+
3
+ * http://github.com/wojtekmach/embedded\_record
4
+
5
+ ## DESCRIPTION:
6
+
7
+ EmbeddedRecord is designed to do two things:
8
+
9
+ * Define records on a Class
10
+ * Embed one or many records (similiar to belongs\_to and
11
+ has\_and\_belongs\_to\_many associations)
12
+
13
+ ## SYNOPSIS:
14
+
15
+ Basic embeddable record usage:
16
+
17
+ ```ruby
18
+ class Color
19
+ include EmbeddedRecord::Record
20
+ attribute :name
21
+ attribute :hex
22
+
23
+ record :red, :name => "Red", :hex => "#FF0000"
24
+ record :green, :name => "Green", :hex => "#00FF00"
25
+ record :blue, :name => "Blue", :hex => "#0000FF"
26
+ end
27
+
28
+ p Color.ids # => [:red, :green, :blue]
29
+ p Color.all # => [#<Color>, #<Color>, #<Color>]
30
+ Color.first
31
+ Color.last
32
+
33
+ blue = Color.find(:blue)
34
+ puts blue.id # => :blue
35
+
36
+ p blue.index # => 2
37
+ p blue.name # => "Blue"
38
+ p blue.hex # => "#0000FF"
39
+
40
+ p blue.is?(:red) # false
41
+ ```
42
+
43
+ Embedding record:
44
+
45
+ ```ruby
46
+ class Car
47
+ attr_accessor :color_mask # integer id of a record
48
+ include EmbeddedRecord
49
+ embed_record :color, :class => Color
50
+ end
51
+
52
+ car = Car.new
53
+ car.color_id = :red
54
+ p car.color_mask # => 0
55
+ p car.color.name # => "Red"
56
+ ```
57
+
58
+ Embedding records:
59
+
60
+ ```ruby
61
+ class Car
62
+ attr_accessor :colors_mask # integer bitmask of record ids
63
+ include EmbeddedRecord
64
+ embed_records :colors, :class => Color, :singular => :color
65
+ end
66
+
67
+ car = Car.new
68
+ car.color_ids = [:red, :green]
69
+ p car.colors.first.name # => "Red"
70
+ p car.color_ids # => [:red, :green]
71
+ p car.colors_mask # => 3 (2**0 + 2**1)
72
+ ```
73
+
74
+ ## REQUIREMENTS:
75
+
76
+ None
77
+
78
+ ## INSTALL:
79
+
80
+ * `gem install embedded\_record`
81
+
82
+ ## LICENSE:
83
+
84
+ (The MIT License)
85
+
86
+ Copyright (c) 2011 Wojciech Mach
87
+
88
+ Permission is hereby granted, free of charge, to any person obtaining
89
+ a copy of this software and associated documentation files (the
90
+ 'Software'), to deal in the Software without restriction, including
91
+ without limitation the rights to use, copy, modify, merge, publish,
92
+ distribute, sublicense, and/or sell copies of the Software, and to
93
+ permit persons to whom the Software is furnished to do so, subject to
94
+ the following conditions:
95
+
96
+ The above copyright notice and this permission notice shall be
97
+ included in all copies or substantial portions of the Software.
98
+
99
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
100
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
101
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
102
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
103
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
104
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
105
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new :test do |t|
5
+ t.pattern = "test/**/*_test.rb"
6
+ end
7
+
8
+ task :default => :test
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "embedded_record"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "embedded_record"
7
+ s.version = EmbeddedRecord::VERSION
8
+ s.authors = ["Wojciech Mach"]
9
+ s.email = ["wojtek@wojtekmach.pl"]
10
+ s.homepage = ""
11
+ s.summary = %q{Embed objects in a bitmask field. Similar to bitmask-attribute and friends}
12
+
13
+ s.rubyforge_project = "embedded_record"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # specify any dependencies here; for example:
21
+ # s.add_development_dependency "rspec"
22
+ # s.add_runtime_dependency "rest-client"
23
+ end
@@ -0,0 +1,201 @@
1
+ module EmbeddedRecord
2
+ VERSION = "0.0.1"
3
+
4
+ def self.included(klass)
5
+ klass.extend self
6
+ end
7
+
8
+ ##
9
+ # Options:
10
+ # - :class - Class to embed
11
+ # - :scope - Boolean wheter to install ActiveRecord scope
12
+ def embed_record(name, options = {})
13
+ raise ArgumentError, "Option :class is required" unless options[:class]
14
+
15
+ klass = options[:class]
16
+ attr = "#{name}_mask"
17
+ all = klass.all
18
+
19
+ define_method name do
20
+ if val = send(attr)
21
+ klass.all[val]
22
+ end
23
+ end
24
+
25
+ define_method "#{name}_id" do
26
+ if val = send(attr)
27
+ klass.all[val].id
28
+ end
29
+ end
30
+
31
+ define_method "#{name}_id=" do |id|
32
+ type_method = embed_id_type_method(klass)
33
+ index = klass.all.index { |obj| obj.id == id.send(type_method) }
34
+ send "#{attr}=", index
35
+ index
36
+ end
37
+
38
+ if options[:scope] == true
39
+ embed_record_scope name
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Options:
45
+ # - class - Class of record
46
+ # - singular - singular form of name
47
+ #
48
+ # Example:
49
+ #
50
+ # class Shirt
51
+ # embed_records :colors, :class => Color, :singular => :color
52
+ # end
53
+ #
54
+ def embed_records(name, options = {})
55
+ [:class, :singular].each do |opt|
56
+ raise ArgumentError, "Option :#{opt} is required" unless options[opt]
57
+ end
58
+
59
+ singular = options[:singular]
60
+ klass = options[:class]
61
+ all_ids = klass.all.map { |obj| obj.id }
62
+ attr = "#{name}_mask"
63
+
64
+ define_method "#{singular}_ids=" do |ids|
65
+ type_method = embed_id_type_method(klass)
66
+ ids = ids.map(&type_method)
67
+ self.send "#{attr}=",
68
+ (ids & all_ids).map { |r| 2**all_ids.index(r) }.inject(0, :+)
69
+ end
70
+
71
+ define_method "#{singular}_ids" do
72
+ all_ids.reject { |r| ((send(attr) || 0) & 2**all_ids.index(r)).zero? }
73
+ end
74
+
75
+ define_method name do
76
+ ids = send("#{singular}_ids")
77
+
78
+ klass.all.select do |obj|
79
+ ids.include? obj.id
80
+ end
81
+ end
82
+
83
+ if options[:scope] == true
84
+ embed_records_scope name
85
+ end
86
+ end
87
+
88
+ def embed_record_scope(name)
89
+ klass = name.to_s.classify.constantize
90
+ plural = name.to_s.pluralize
91
+
92
+ send :scope, :"with_#{name.to_s.tableize}", lambda { |*ids|
93
+ masks = ids.map { |id| klass.find(id).index }
94
+ where("#{plural}_mask in (?)", masks)
95
+ }
96
+ end
97
+
98
+ def embed_records_scope(name)
99
+ klass = name.to_s.classify.constantize
100
+ plural = name.to_s.pluralize
101
+
102
+ send :scope, :"with_#{name.to_s.tableize}", lambda { |*ids|
103
+ masks = ids.map { |id| 2 ** klass.find(id).index }.join(" | ")
104
+ where("#{plural}_mask & (?)", masks)
105
+ }
106
+ end
107
+
108
+ private
109
+
110
+ def embed_id_type_method(klass)
111
+ klass.ids.first.is_a?(Symbol) ? :to_sym : :to_s
112
+ end
113
+ end
114
+
115
+ module EmbeddedRecord::Record
116
+ def self.included(klass)
117
+ klass.extend ClassMethods
118
+ klass.attribute :id
119
+ end
120
+
121
+ ##
122
+ # Returns true if record's id is the same as argument or false otherwise
123
+ def is?(id)
124
+ self.id == id
125
+ end
126
+
127
+ ##
128
+ # Returns Integer index of the record in the records set
129
+ def index
130
+ self.class.all.index(self)
131
+ end
132
+
133
+ module ClassMethods
134
+ ##
135
+ # Returns Array of record attributes
136
+ def attributes
137
+ @attributes ||= []
138
+ end
139
+
140
+ ##
141
+ # Returns Array of records ids
142
+ def ids
143
+ records.keys
144
+ end
145
+
146
+ ##
147
+ # Returns record for a given id or nil
148
+ def find(id)
149
+ all.find { |obj| obj.id == id }
150
+ end
151
+
152
+ ##
153
+ # Returns all records
154
+ def all
155
+ records.values
156
+ end
157
+
158
+ ##
159
+ # Returns first record
160
+ def first
161
+ all.first
162
+ end
163
+
164
+ ##
165
+ # Returns last record
166
+ def last
167
+ all.last
168
+ end
169
+
170
+ ##
171
+ # Defines a record with an id and Hash of attributes.
172
+ def record(id, attrs = {})
173
+ record = new
174
+ record.send "id=", id
175
+ attrs.each do |k, v|
176
+ unless attributes.include? k
177
+ raise ArgumentError, "Atrribute '#{k}' not found"
178
+ end
179
+
180
+ record.send "#{k}=", v
181
+ end
182
+ records[id] = record
183
+ end
184
+
185
+ ##
186
+ # Defines a Symbol attribute for a record
187
+ def attribute(name)
188
+ attributes << name
189
+
190
+ unless method_defined?(name)
191
+ attr_accessor name
192
+ end
193
+ end
194
+
195
+ private
196
+
197
+ def records
198
+ @records ||= {}
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,139 @@
1
+ gem "minitest"
2
+ require "minitest/spec"
3
+ require "minitest/autorun"
4
+ require "embedded_record"
5
+
6
+ class Color
7
+ include EmbeddedRecord::Record
8
+
9
+ attribute :name
10
+
11
+ record :red, :name => "Red"
12
+ record :green, :name => "Green"
13
+ record :blue, :name => "Blue"
14
+ end
15
+
16
+ describe "Object with EmbeddedRecord::Record" do
17
+ it "#is? returns true if id is the same" do
18
+ Color.find(:green).is?(:green).must_equal true
19
+ end
20
+
21
+ it "#is? returns false if id is not the same" do
22
+ Color.find(:green).is?(:red).must_equal false
23
+ end
24
+
25
+ it "#index return record index in record set" do
26
+ Color.find(:red).index.must_equal 0
27
+ Color.find(:green).index.must_equal 1
28
+ Color.find(:blue).index.must_equal 2
29
+ end
30
+ end
31
+
32
+ describe "Class with EmbeddedRecord::Record" do
33
+ it "::attributes returns names of record attributes" do
34
+ Color.attributes.must_equal [:id, :name]
35
+ end
36
+
37
+ it "::all returns all records" do
38
+ all = Color.all
39
+ all.must_be_kind_of Array
40
+ all.size.must_equal 3
41
+ all.first.must_be_kind_of Color
42
+ all.first.name.must_equal "Red"
43
+ end
44
+
45
+ it "::ids returns records ids" do
46
+ Color.ids.must_equal [:red, :green, :blue]
47
+ end
48
+
49
+ it "::find returns record with specified id" do
50
+ Color.find(:green).id.must_equal :green
51
+ end
52
+
53
+ it "::find returns nil when no record is found for an id" do
54
+ Color.find(:bad).must_equal nil
55
+ end
56
+
57
+ it "::first returns first record" do
58
+ Color.first.id.must_equal :red
59
+ end
60
+
61
+ it "::last returns last record" do
62
+ Color.last.id.must_equal :blue
63
+ end
64
+ end
65
+
66
+ describe "Class with EmbeddedRecord" do
67
+ before do
68
+ @cls = Class.new
69
+ @cls.send :include, EmbeddedRecord
70
+ end
71
+
72
+ it "#embed_record defines <name>, <name>_id, <name>_id= methods" do
73
+ @cls.embed_record :color, :class => Color
74
+ @cls.method_defined?(:color).must_equal true
75
+ @cls.method_defined?(:color_id).must_equal true
76
+ @cls.method_defined?(:color_id=).must_equal true
77
+ end
78
+
79
+ it "#embed_records defines <name>, <name>_ids, <name>_ids= methods" do
80
+ @cls.embed_records :colors, :class => Color, :singular => :color
81
+ @cls.method_defined?(:colors).must_equal true
82
+ @cls.method_defined?(:color_ids).must_equal true
83
+ @cls.method_defined?(:color_ids=).must_equal true
84
+ end
85
+ end
86
+
87
+ class Car
88
+ include EmbeddedRecord
89
+ embed_record :color, :class => Color
90
+ attr_accessor :color_mask
91
+ end
92
+
93
+ describe "Object embedding record" do
94
+ before do
95
+ @car = Car.new
96
+ end
97
+
98
+ it "#<name>_id= sets the index of a record" do
99
+ @car.color_id = :green
100
+ @car.color_mask.must_equal Color.find(:green).index
101
+ end
102
+
103
+ it "#<name>_id returns id of a record" do
104
+ @car.color_mask = Color.find(:blue).index
105
+ @car.color_id.must_equal :blue
106
+ end
107
+
108
+ it "#<name> returns record" do
109
+ @car.color_mask = Color.find(:blue).index
110
+ @car.color.must_equal Color.find(:blue)
111
+ end
112
+ end
113
+
114
+ class Shirt
115
+ include EmbeddedRecord
116
+ embed_records :colors, :class => Color, :singular => "color"
117
+ attr_accessor :colors_mask
118
+ end
119
+
120
+ describe "Object embedding records" do
121
+ before do
122
+ @shirt = Shirt.new
123
+ end
124
+
125
+ it "#<name>_ids= sets the indices of records" do
126
+ @shirt.color_ids = [:red, :blue]
127
+ @shirt.colors_mask.must_equal(2**0 + 2**2)
128
+ end
129
+
130
+ it "#<name>_ids returns ids of records" do
131
+ @shirt.colors_mask = 2**1 + 2**2
132
+ @shirt.color_ids = [:green, :blue]
133
+ end
134
+
135
+ it "#<name> returns records" do
136
+ @shirt.color_ids = [:green, :blue]
137
+ @shirt.colors.must_equal [Color.find(:green), Color.find(:blue)]
138
+ end
139
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: embedded_record
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Wojciech Mach
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-06 00:00:00.000000000Z
13
+ dependencies: []
14
+ description:
15
+ email:
16
+ - wojtek@wojtekmach.pl
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - README.md
24
+ - Rakefile
25
+ - embedded_record.gemspec
26
+ - lib/embedded_record.rb
27
+ - test/embedded_record_test.rb
28
+ homepage: ''
29
+ licenses: []
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project: embedded_record
48
+ rubygems_version: 1.8.10
49
+ signing_key:
50
+ specification_version: 3
51
+ summary: Embed objects in a bitmask field. Similar to bitmask-attribute and friends
52
+ test_files:
53
+ - test/embedded_record_test.rb