rubyhaze-persisted 0.0.1-jruby

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Adrian Madrid
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,42 @@
1
+ = rubyhaze-persisted
2
+
3
+ RubyHaze Persisted is a little gem that helps you persist and search your (j)ruby objects into Hazelcast distributed maps.
4
+
5
+ == Getting started
6
+
7
+ Let's get some distributed ruby objects going:
8
+
9
+ shell> rvm jruby
10
+ shell> gem install rubyhaze-persisted
11
+ shell> rubyhaze_console
12
+
13
+ require 'rubyhaze-persisted'
14
+
15
+ class Foo
16
+ include RubyHaze::Persisted
17
+ field :name, :string
18
+ field :age, :int
19
+ end
20
+
21
+ a = Foo.create :name => "Raffaello", :age => 32
22
+ b = Foo.create :name => "Leonardo", :age => 45
23
+ c = Foo.create :name => "Michelangelo", :age => 65
24
+
25
+ found = Foo.find "age < 60 AND name LIKE '%lo'"
26
+ found.first.name
27
+
28
+ >> "Raffaello"
29
+
30
+ == Note on Patches/Pull Requests
31
+
32
+ * Fork the project.
33
+ * Make your feature addition or bug fix.
34
+ * Add tests for it. This is important so I don't break it in a
35
+ future version unintentionally.
36
+ * Commit, do not mess with rakefile, version, or history.
37
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
38
+ * Send me a pull request. Bonus points for topic branches.
39
+
40
+ == Copyright
41
+
42
+ Copyright (c) 2010 Adrian Madrid. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rubyhaze-persisted"
8
+ gem.summary = %Q{ActiveRecord-like objects persisted with Hazelcast and RubyHaze}
9
+ gem.description = %Q{Have your distributed Ruby objects and search them too.}
10
+ gem.email = "aemadrid@alliancehealth.com"
11
+ gem.homepage = "http://github.com/aemadrid/rubyhaze-persisted"
12
+ gem.authors = ["Adrian Madrid"]
13
+ gem.files = FileList['bin/*', 'lib/**/*.rb', 'test/**/*.rb', '[A-Z]*'].to_a
14
+ gem.test_files = Dir["test/test*.rb"]
15
+ gem.platform = "jruby"
16
+ gem.add_dependency "bitescript"
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/test_*.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :test => :check_dependencies
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "rubyhaze-persisted #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,218 @@
1
+ require "rubyhaze"
2
+
3
+ gem "activesupport"
4
+
5
+ require "active_support/core_ext/module/attr_accessor_with_default"
6
+ require "active_support/core_ext/object/blank"
7
+ require "active_support/concern"
8
+ require "active_support/callbacks"
9
+
10
+ require "active_model"
11
+
12
+ module RubyHaze
13
+ module Persisted
14
+
15
+ extend ActiveSupport::Concern
16
+ include ActiveSupport::Callbacks
17
+
18
+ extend ActiveModel::Naming
19
+ extend ActiveModel::Translation
20
+ extend ActiveModel::Callbacks
21
+
22
+ include ActiveModel::AttributeMethods
23
+ include ActiveModel::Conversion
24
+ include ActiveModel::Dirty
25
+ include ActiveModel::Serialization
26
+ include ActiveModel::Serializers::JSON
27
+ include ActiveModel::Serializers::Xml
28
+ include ActiveModel::Validations
29
+
30
+ included do
31
+ attribute_method_suffix '', '=', '?'
32
+ extend ClassMethods
33
+ end
34
+
35
+ module InstanceMethods
36
+
37
+ def initialize(options = {})
38
+ options.each { |name, value| send "#{name}=", value }
39
+ @callbacks = []
40
+ @new_record = true
41
+ @destroyed = false
42
+ end
43
+
44
+ def new_record?
45
+ @new_record
46
+ end
47
+
48
+ def persisted?
49
+ !(new_record? || destroyed?)
50
+ end
51
+
52
+ def destroyed?
53
+ @destroyed
54
+ end
55
+
56
+ def attribute(key)
57
+ instance_variable_get("@#{key}")
58
+ end
59
+
60
+ def attribute=(key, value)
61
+ send("#{key}_will_change!") unless value == attribute(key)
62
+ instance_variable_set("@#{key}", value)
63
+ end
64
+
65
+ def attribute?(key)
66
+ instance_variable_get("@#{key}").present?
67
+ end
68
+
69
+ def attribute_names
70
+ self.class.attribute_names
71
+ end
72
+
73
+ def attribute_types
74
+ self.class.attribute_types
75
+ end
76
+
77
+ def attribute_options
78
+ self.class.attribute_options
79
+ end
80
+
81
+ def attributes
82
+ attrs = {}
83
+ attribute_names.each { |name| attrs[name.to_s] = attribute(name) }
84
+ attrs
85
+ end
86
+
87
+ def values
88
+ attribute_names.map { |name| attribute(name) }
89
+ end
90
+
91
+ def to_ary
92
+ attribute_names.map { |name| [name.to_s, instance_variable_get("@#{name}")] }.unshift ['class', self.class.name]
93
+ end
94
+
95
+ def ==(other)
96
+ return false unless other.respond_to? :to_ary
97
+ to_ary == other.to_ary
98
+ end
99
+
100
+ def shadow_object
101
+ self.class.map_java_class.new *values
102
+ end
103
+
104
+ def load_shadow_object(shadow)
105
+ attribute_names.each do |name|
106
+ send "attribute=", name, shadow.send(name)
107
+ end
108
+ self
109
+ end
110
+
111
+ def save
112
+ create_or_update
113
+ end
114
+
115
+ def save!
116
+ create_or_update || raise("Not saved")
117
+ end
118
+
119
+ def create_or_update
120
+ result = new_record? ? create : update
121
+ result != false
122
+ end
123
+
124
+ def update
125
+ raise "Missing uid" unless uid?
126
+ self.class.map[uid] = shadow_object
127
+ @previously_changed = changes
128
+ @changed_attributes.clear
129
+ true
130
+ end
131
+
132
+ def create
133
+ @uid ||= RubyHaze.random_uuid
134
+ self.class.map[uid] = shadow_object
135
+ @previously_changed = changes
136
+ @changed_attributes.clear
137
+ @new_record = false
138
+ uid
139
+ end
140
+
141
+ def load
142
+ raise "Missing uid for load" if uid.blank?
143
+ found = self.class.map[uid]
144
+ raise "Record not found" unless found
145
+ load_shadow_object(found)
146
+ @changed_attributes.clear
147
+ @new_record = false
148
+ self
149
+ end
150
+ alias :reload :load
151
+ alias :reset :load
152
+
153
+ def to_s
154
+ "<#{self.class.name}:#{object_id} #{to_ary[1..-1].map { |k, v| "#{k}=#{v}" }.join(" ")} >"
155
+ end
156
+
157
+ alias :inspect :to_s
158
+
159
+ def to_key
160
+ persisted? ? [uid] : nil
161
+ end
162
+
163
+ end
164
+
165
+ module ClassMethods
166
+
167
+ def create(options = {})
168
+ obj = new options
169
+ obj.save
170
+ obj
171
+ end
172
+
173
+ def map_java_class_name
174
+ 'RubyHaze_Shadow__' + name
175
+ end
176
+
177
+ def map_java_class
178
+ @java_class ||= RubyHaze::Persisted::ShadowClassGenerator.get map_java_class_name, attributes
179
+ end
180
+
181
+ def map
182
+ @map ||= RubyHaze::Map.new map_java_class_name
183
+ end
184
+
185
+ def attributes
186
+ @attributes ||= [[:uid, :string, {}]]
187
+ end
188
+
189
+ def attribute_names()
190
+ attributes.map { |ary| ary[0] }
191
+ end
192
+
193
+ def attribute_types()
194
+ attributes.map { |ary| ary[1] }
195
+ end
196
+
197
+ def attribute_options()
198
+ attributes.map { |ary| ary[2] }
199
+ end
200
+
201
+ def attribute(name, type, options = {})
202
+ raise "Attribute [#{name}] already defined" if attribute_names.include?(name)
203
+ @attributes << [name, type, options]
204
+ # attr_accessor name
205
+ end
206
+
207
+ def find(predicate)
208
+ map.values(predicate).map { |shadow| new.load_shadow_object shadow }
209
+ end
210
+
211
+ def find_uids(predicate)
212
+ map.keys(predicate)
213
+ end
214
+
215
+ end
216
+
217
+ end
218
+ end
@@ -0,0 +1,64 @@
1
+ require 'bitescript'
2
+
3
+ module RubyHaze
4
+ module Persisted
5
+ module ShadowClassGenerator
6
+
7
+ def self.get(name, attributes)
8
+ RubyHaze::Persisted.const_defined?(name) ?
9
+ RubyHaze::Persisted.const_get(name) :
10
+ generate(name, attributes)
11
+ end
12
+
13
+ def self.attribute_load_types
14
+ {
15
+ :string => :aload,
16
+ :int => :iload,
17
+ :boolean => :iload,
18
+ :double => :dload,
19
+ }
20
+ end
21
+
22
+ def self.generate(name, attributes)
23
+ tmp_path = (ENV['RUBYHAZE_PERSISTED_TMP_PATH'] || File.join(Dir.pwd, 'tmp'))
24
+ builder = BiteScript::FileBuilder.new name + '.class'
25
+ class_dsl = []
26
+ class_dsl << %{public_class "#{name}", object, Java::JavaIo::Serializable do}
27
+ attributes.each do |attr_name, type, options|
28
+ class_dsl << %{ public_field :#{attr_name}, send(:#{type})}
29
+ end
30
+ class_dsl << %{ public_constructor [], #{attributes.map { |ary| ary[1].to_s }.join(', ')} do}
31
+ class_dsl << %{ aload 0}
32
+ class_dsl << %{ invokespecial object, "<init>", [void]}
33
+ index = 1
34
+ attributes.each do |attr_name, type, options|
35
+ class_dsl << %{ aload 0}
36
+ class_dsl << %{ #{attribute_load_types[type]} #{index}}
37
+ index += 1
38
+ class_dsl << %{ putfield this, :#{attr_name}, send(:#{type})}
39
+ end
40
+ class_dsl << %{ returnvoid}
41
+ class_dsl << %{ end}
42
+ class_dsl << %{end}
43
+ class_dsl = class_dsl.join("\n")
44
+ if $DEBUG
45
+ FileUtils.mkdir_p tmp_path
46
+ filename = File.join tmp_path, name + '.bc'
47
+ File.open(filename, 'w') { |file| file.write class_dsl }
48
+ end
49
+ builder.instance_eval class_dsl, __FILE__, __LINE__
50
+ builder.generate do |builder_filename, class_builder|
51
+ bytes = class_builder.generate
52
+ klass = JRuby.runtime.jruby_class_loader.define_class name, bytes.to_java_bytes
53
+ if $DEBUG
54
+ filename = File.join tmp_path, builder_filename
55
+ File.open(filename, 'w') { |file| file.write class_builder.generate }
56
+ end
57
+ RubyHaze::Persisted.const_set name, JavaUtilities.get_proxy_class(klass.name)
58
+ end
59
+ RubyHaze::Persisted.const_get name
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,12 @@
1
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'persisted'))
2
+ require 'shadow_class_generator'
3
+ require 'model'
4
+
5
+ class D
6
+ include RubyHaze::Persisted
7
+ attribute :name, :string
8
+ attribute :age, :int
9
+
10
+
11
+ validates_presence_of :name
12
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'test/unit'
2
+ require 'forwardable'
3
+
4
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/rubyhaze/persisted')
5
+
6
+ class Test::Unit::TestCase
7
+ end
8
+
9
+ # Start a new hazelcast cluster
10
+ RubyHaze.init :group => { :username => "test_persisted", :password => "test_persisted" }
11
+
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/helper')
2
+
3
+ class LintTest < ActiveModel::TestCase
4
+ include ActiveModel::Lint::Tests
5
+
6
+ class PersistedTestModel
7
+ include RubyHaze::Persisted
8
+ attribute :name, :string
9
+ end
10
+
11
+ def setup
12
+ @model = PersistedTestModel.new
13
+ end
14
+ end
@@ -0,0 +1,83 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/helper')
2
+
3
+ class Foo
4
+ include RubyHaze::Persisted
5
+ attribute :name, :string
6
+ attribute :age, :int
7
+ end unless defined? Foo
8
+
9
+ class TestRubyHazePersistedClassMethods < Test::Unit::TestCase
10
+
11
+ def test_right_store
12
+ store = Foo.map
13
+ assert_equal store.class.name, "RubyHaze::Map"
14
+ assert_equal store.name, "RubyHaze_Shadow__Foo"
15
+ assert_equal store.name, RubyHaze::Map.new("RubyHaze_Shadow__Foo").name
16
+ end
17
+
18
+ def test_right_fields
19
+ fields = Foo.attributes
20
+ assert_equal fields, [[:uid, :string, {}], [:name, :string, {}], [:age, :int, {}]]
21
+ assert_equal Foo.attribute_names, [:uid, :name, :age]
22
+ assert_equal Foo.attribute_types, [:string, :string, :int]
23
+ assert_equal Foo.attribute_options, [{}, {}, {}]
24
+ end
25
+
26
+ def test_right_shadow_class
27
+ assert_equal Foo.map_java_class_name, "RubyHaze_Shadow__Foo"
28
+ assert_equal Foo.map_java_class.name, "Java::Default::RubyHaze_Shadow__Foo"
29
+ end
30
+
31
+ end
32
+
33
+ class TestRubyHazePersistedStorage < Test::Unit::TestCase
34
+
35
+ def test_store_reload_objects
36
+ Foo.map.clear
37
+ assert_equal Foo.map.size, 0
38
+ @a = Foo.create :name => "Leonardo", :age => 65
39
+ assert_equal Foo.map.size, 1
40
+ @b = Foo.create :name => "Michelangelo", :age => 45
41
+ assert_equal Foo.map.size, 2
42
+ @b.age = 47
43
+ @b.reload
44
+ assert_equal Foo.map.size, 2
45
+ assert_equal @b.age, 45
46
+ Foo.map.clear
47
+ assert_equal Foo.map.size, 0
48
+ end
49
+
50
+ def test_find_through_predicates
51
+ Foo.map.clear
52
+ @a = Foo.create :name => "Leonardo", :age => 65
53
+ @b = Foo.create :name => "Michelangelo", :age => 45
54
+ @c = Foo.create :name => "Raffaello", :age => 32
55
+
56
+ res = Foo.find 'age < 40'
57
+ assert_equal res.size, 1
58
+ assert_equal res.first, @c
59
+ assert_equal res.first.name, @c.name
60
+
61
+ res = Foo.find 'age BETWEEN 40 AND 50'
62
+ assert_equal res.size, 1
63
+ assert_equal res.first, @b
64
+ assert_equal res.first.name, @b.name
65
+
66
+ res = Foo.find "name LIKE 'Leo%'"
67
+ assert_equal res.size, 1
68
+ assert_equal res.first, @a
69
+ assert_equal res.first.name, @a.name
70
+
71
+ # res = Foo.find "age IN (32, 65)"
72
+ # assert_equal res.size, 2
73
+ # names = res.map{|x| x.name }.sort
74
+ # assert_equal names.first, @a.name
75
+ # assert_equal names.last, @b.name
76
+
77
+ res = Foo.find "age < 40 AND name LIKE '%o'"
78
+ assert_equal res.size, 1
79
+ assert_equal res.first, @c
80
+ assert_equal res.first.name, @c.name
81
+ end
82
+
83
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubyhaze-persisted
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: jruby
11
+ authors:
12
+ - Adrian Madrid
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-30 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: bitescript
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ description: Have your distributed Ruby objects and search them too.
33
+ email: aemadrid@alliancehealth.com
34
+ executables: []
35
+
36
+ extensions: []
37
+
38
+ extra_rdoc_files:
39
+ - LICENSE
40
+ - README.rdoc
41
+ files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ - Rakefile
45
+ - VERSION
46
+ - lib/rubyhaze/persisted.rb
47
+ - lib/rubyhaze/persisted/model.rb
48
+ - lib/rubyhaze/persisted/shadow_class_generator.rb
49
+ - test/helper.rb
50
+ - test/test_model.rb
51
+ - test/test_persisted.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/aemadrid/rubyhaze-persisted
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.6
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: ActiveRecord-like objects persisted with Hazelcast and RubyHaze
82
+ test_files:
83
+ - test/test_model.rb
84
+ - test/test_persisted.rb