ripple 0.8.2 → 0.8.3

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/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source :gemcutter
2
+
3
+ gem 'bundler'
4
+ gem 'riak-client', :path => "../riak-client"
5
+ gem 'activemodel', '~>3.0.0'
6
+ gem 'rspec', '~>2.0.0'
7
+ gem 'tzinfo'
8
+ gem 'rake'
9
+
10
+ if defined? JRUBY_VERSION
11
+ gem 'json'
12
+ gem 'jruby-openssl'
13
+ else
14
+ gem 'yajl-ruby'
15
+ gem 'curb', '>0.6'
16
+ end
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ gemspec = Gem::Specification.new do |gem|
8
8
  gem.summary = %Q{ripple is an object-mapper library for Riak, the distributed database by Basho.}
9
9
  gem.description = %Q{ripple is an object-mapper library for Riak, the distributed database by Basho. It uses ActiveModel to provide an experience that integrates well with Rails 3 applications.}
10
10
  gem.version = version
11
- gem.email = "seancribbs@gmail.com"
11
+ gem.email = "sean@basho.com"
12
12
  gem.homepage = "http://seancribbs.github.com/ripple"
13
13
  gem.authors = ["Sean Cribbs"]
14
14
  gem.add_development_dependency "rspec", "~>2.0.0"
@@ -16,22 +16,121 @@ gemspec = Gem::Specification.new do |gem|
16
16
  gem.add_dependency "activesupport", "~>3.0.0"
17
17
  gem.add_dependency "activemodel", "~>3.0.0"
18
18
 
19
- files = FileList["**/*"]
20
- files.exclude /\.DS_Store/
21
- files.exclude /\#/
22
- files.exclude /~/
23
- files.exclude /\.swp/
24
- files.exclude '**/._*'
25
- files.exclude '**/*.orig'
26
- files.exclude '**/*.rej'
27
- files.exclude /^pkg/
28
- files.exclude 'ripple.gemspec'
29
- files.exclude 'Gemfile'
30
- files.exclude 'spec/support/test_server.yml'
19
+ gem.files = %W{
20
+ Gemfile
21
+ lib/rails/generators/ripple/configuration/configuration_generator.rb
22
+ lib/rails/generators/ripple/configuration/templates
23
+ lib/rails/generators/ripple/configuration/templates/ripple.yml
24
+ lib/rails/generators/ripple/js/js_generator.rb
25
+ lib/rails/generators/ripple/js/templates/js/contrib.js
26
+ lib/rails/generators/ripple/js/templates/js/ripple.js
27
+ lib/rails/generators/ripple/model/model_generator.rb
28
+ lib/rails/generators/ripple/model/templates
29
+ lib/rails/generators/ripple/model/templates/model.rb
30
+ lib/rails/generators/ripple/test/templates/test_server.rb
31
+ lib/rails/generators/ripple/test/test_generator.rb
32
+ lib/rails/generators/ripple_generator.rb
33
+ lib/ripple/associations/embedded.rb
34
+ lib/ripple/associations/instantiators.rb
35
+ lib/ripple/associations/linked.rb
36
+ lib/ripple/associations/many.rb
37
+ lib/ripple/associations/many_embedded_proxy.rb
38
+ lib/ripple/associations/many_linked_proxy.rb
39
+ lib/ripple/associations/one.rb
40
+ lib/ripple/associations/one_embedded_proxy.rb
41
+ lib/ripple/associations/one_linked_proxy.rb
42
+ lib/ripple/associations/proxy.rb
43
+ lib/ripple/associations.rb
44
+ lib/ripple/attribute_methods/dirty.rb
45
+ lib/ripple/attribute_methods/query.rb
46
+ lib/ripple/attribute_methods/read.rb
47
+ lib/ripple/attribute_methods/write.rb
48
+ lib/ripple/attribute_methods.rb
49
+ lib/ripple/callbacks.rb
50
+ lib/ripple/conversion.rb
51
+ lib/ripple/core_ext/casting.rb
52
+ lib/ripple/core_ext.rb
53
+ lib/ripple/document/bucket_access.rb
54
+ lib/ripple/document/finders.rb
55
+ lib/ripple/document/key.rb
56
+ lib/ripple/document/persistence.rb
57
+ lib/ripple/document.rb
58
+ lib/ripple/embedded_document/finders.rb
59
+ lib/ripple/embedded_document/persistence.rb
60
+ lib/ripple/embedded_document.rb
61
+ lib/ripple/i18n.rb
62
+ lib/ripple/inspection.rb
63
+ lib/ripple/locale
64
+ lib/ripple/locale/en.yml
65
+ lib/ripple/nested_attributes.rb
66
+ lib/ripple/properties.rb
67
+ lib/ripple/property_type_mismatch.rb
68
+ lib/ripple/railtie.rb
69
+ lib/ripple/timestamps.rb
70
+ lib/ripple/translation.rb
71
+ lib/ripple/validations/associated_validator.rb
72
+ lib/ripple/validations.rb
73
+ lib/ripple.rb
74
+ Rakefile
75
+ ripple.gemspec
76
+ spec/fixtures/config.yml
77
+ spec/integration/ripple/associations_spec.rb
78
+ spec/integration/ripple/nested_attributes_spec.rb
79
+ spec/integration/ripple/persistence_spec.rb
80
+ spec/ripple/associations/many_embedded_proxy_spec.rb
81
+ spec/ripple/associations/many_linked_proxy_spec.rb
82
+ spec/ripple/associations/one_embedded_proxy_spec.rb
83
+ spec/ripple/associations/one_linked_proxy_spec.rb
84
+ spec/ripple/associations/proxy_spec.rb
85
+ spec/ripple/associations_spec.rb
86
+ spec/ripple/attribute_methods_spec.rb
87
+ spec/ripple/bucket_access_spec.rb
88
+ spec/ripple/callbacks_spec.rb
89
+ spec/ripple/conversion_spec.rb
90
+ spec/ripple/core_ext_spec.rb
91
+ spec/ripple/document_spec.rb
92
+ spec/ripple/embedded_document/finders_spec.rb
93
+ spec/ripple/embedded_document/persistence_spec.rb
94
+ spec/ripple/embedded_document_spec.rb
95
+ spec/ripple/finders_spec.rb
96
+ spec/ripple/inspection_spec.rb
97
+ spec/ripple/key_spec.rb
98
+ spec/ripple/persistence_spec.rb
99
+ spec/ripple/properties_spec.rb
100
+ spec/ripple/ripple_spec.rb
101
+ spec/ripple/timestamps_spec.rb
102
+ spec/ripple/validations_spec.rb
103
+ spec/spec_helper.rb
104
+ spec/support/associations/proxies.rb
105
+ spec/support/mocks.rb
106
+ spec/support/models/address.rb
107
+ spec/support/models/box.rb
108
+ spec/support/models/car.rb
109
+ spec/support/models/cardboard_box.rb
110
+ spec/support/models/clock.rb
111
+ spec/support/models/customer.rb
112
+ spec/support/models/driver.rb
113
+ spec/support/models/email.rb
114
+ spec/support/models/engine.rb
115
+ spec/support/models/family.rb
116
+ spec/support/models/favorite.rb
117
+ spec/support/models/invoice.rb
118
+ spec/support/models/late_invoice.rb
119
+ spec/support/models/note.rb
120
+ spec/support/models/page.rb
121
+ spec/support/models/paid_invoice.rb
122
+ spec/support/models/passenger.rb
123
+ spec/support/models/seat.rb
124
+ spec/support/models/tasks.rb
125
+ spec/support/models/tree.rb
126
+ spec/support/models/user.rb
127
+ spec/support/models/wheel.rb
128
+ spec/support/models/widget.rb
129
+ spec/support/test_server.rb
130
+ spec/support/test_server.yml.example
131
+ }
31
132
 
32
- gem.files = files.to_a
33
-
34
- gem.test_files = FileList["spec/**/*.rb"].to_a
133
+ gem.test_files = gem.files.grep(/_spec\.rb$/)
35
134
  end
36
135
 
37
136
  # Gem packaging tasks
@@ -0,0 +1,26 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require 'rails/generators/ripple_generator'
15
+
16
+ module Ripple
17
+ module Generators
18
+ class ConfigurationGenerator < Base
19
+ desc 'Generates a configuration file for Ripple.'
20
+
21
+ def create_configuration_file
22
+ template 'ripple.yml', 'config/ripple.yml'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # Configure Riak connections for the Ripple library.
2
+ development:
3
+ port: 8098
4
+ host: localhost
5
+
6
+ # The test environment has additional keys for configuring the
7
+ # Riak::TestServer for your test/spec suite:
8
+ #
9
+ # * bin_dir specifies the path to the "riak" script that you use to
10
+ # start Riak (just the directory)
11
+ # * js_source_dir specifies where your custom Javascript functions for
12
+ # MapReduce should be loaded from. Usually app/mapreduce.
13
+ test:
14
+ port: 9000
15
+ host: localhost
16
+ bin_dir: /usr/local/bin # Default for Homebrew.
17
+ js_source_dir: <%%= Rails.root + "app/mapreduce" %>
18
+
19
+ production:
20
+ port: 8098
21
+ host: localhost
@@ -0,0 +1,26 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require 'rails/generators/ripple_generator'
15
+
16
+ module Ripple
17
+ module Generators
18
+ class JsGenerator < Base
19
+ desc 'Generates Javascript built-ins for use in your queries.'
20
+
21
+ def create_js_files
22
+ directory 'js', 'app/mapreduce'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,80 @@
1
+ // -------------------------------------------------------------------
2
+ //
3
+ //
4
+ // This file is provided to you under the Apache License,
5
+ // Version 2.0 (the "License"); you may not use this file
6
+ // except in compliance with the License. You may obtain
7
+ // a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing,
12
+ // software distributed under the License is distributed on an
13
+ // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ // KIND, either express or implied. See the License for the
15
+ // specific language governing permissions and limitations
16
+ // under the License.
17
+ //
18
+ // -------------------------------------------------------------------
19
+ //
20
+ // This file contains Javascript MapReduce functions copied from the
21
+ // "Riak Function Contrib" project.
22
+ //
23
+ Riak.Contrib = {
24
+ // Count keys in a group of results.
25
+ // http://contrib.basho.com/count_keys.html
26
+ mapCount: function(){
27
+ return [1];
28
+ },
29
+
30
+ // Generate commonly used statistics from an array of numbers.
31
+ // Supports count, sum, min, max, percentiles, mean, variance, and
32
+ // stddev.
33
+ // http://contrib.basho.com/stats.html
34
+ reduceStats: function(data) {
35
+ var result = {};
36
+
37
+ data.sort(function(a,b){return a-b;});
38
+ result.count = data.length;
39
+
40
+ // Since the data is sorted, the minimum value
41
+ // is at the beginning of the array, the median
42
+ // value is in the middle of the array, and the
43
+ // maximum value is at the end of the array.
44
+ result.min = data[0];
45
+ result.max = data[data.length - 1];
46
+
47
+ var ntileFunc = function(percentile){
48
+ if (data.length == 1) return data[0];
49
+ var ntileRank = ((percentile/100) * (data.length - 1)) + 1;
50
+ var integralRank = Math.floor(ntileRank);
51
+ var fractionalRank = ntileRank - integralRank;
52
+ var lowerValue = data[integralRank-1];
53
+ var upperValue = data[integralRank];
54
+ return (fractionalRank * (upperValue - lowerValue)) + lowerValue;
55
+ };
56
+
57
+ result.percentile25 = ntileFunc(25);
58
+ result.median = ntileFunc(50);
59
+ result.percentile75 = ntileFunc(75);
60
+ result.percentile99 = ntileFunc(99);
61
+
62
+ // Compute the mean and variance using a
63
+ // numerically stable algorithm.
64
+ var sqsum = 0;
65
+ result.mean = data[0];
66
+ result.sum = result.mean * result.count;
67
+ for (var i = 1; i < data.length; ++i) {
68
+ var x = data[i];
69
+ var delta = x - result.mean;
70
+ var sweep = i + 1.0;
71
+ result.mean += delta / sweep;
72
+ sqsum += delta * delta * (i / sweep);
73
+ result.sum += x;
74
+ }
75
+ result.variance = sqsum / result.count;
76
+ result.sdev = Math.sqrt(result.variance);
77
+
78
+ return result;
79
+ }
80
+ };
@@ -0,0 +1,145 @@
1
+ // Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ /*
16
+ * Namespace for Ripple's built-in MapReduce functions.
17
+ */
18
+ var Ripple = {
19
+ /*
20
+ * Filter input values by simple equality test on passed "fields"
21
+ * argument. Returns bucket/key pairs for use in later map phases.
22
+ *
23
+ * Raw Phase Example:
24
+ * {"map":{
25
+ * "language":"javascript",
26
+ * "name":"Ripple.filterByFields",
27
+ * "arg":{"manufactured":true, "name":"widget"}
28
+ * }}
29
+ *
30
+ * Ruby invocation example:
31
+ * mr.map("Ripple.filterByFields",
32
+ * :arg => {:manufactured => true, :name => "widget"})
33
+ *
34
+ */
35
+ filterByFields: function(value, keyData, fields){
36
+ var object = Riak.mapValuesJson(value)[0];
37
+ for(field in fields){
38
+ if(object[field] != fields[field])
39
+ return [];
40
+ }
41
+ return [[value.bucket, value.key]];
42
+ },
43
+ /*
44
+ * Filter input values by various conditions passed as the
45
+ * "conditions" argument. Returns bucket/key pairs for use in
46
+ * later map phases.
47
+ *
48
+ * Valid operators:
49
+ * ==, eq (equality)
50
+ * !=, neq (inequality)
51
+ * <, lt (less than)
52
+ * =<, <=, lte (less than or equal)
53
+ * >, gt (greater than)
54
+ * >=, =>, gte (greater than or equal)
55
+ * ~=, match (regular expression match)
56
+ * between (inclusive numeric range)
57
+ * includes (String or Array inclusion)
58
+ *
59
+ * Example:
60
+ * {"map":{
61
+ * "language":"javascript",
62
+ * "name":"Ripple.filterByConditions",
63
+ * "arg":{"tags":{"includes":"riak"}, "title":{"~=":"schema"}}
64
+ * }}
65
+ *
66
+ * Ruby invocation example:
67
+ * mr.map("Ripple.filterByConditions",
68
+ * :arg => {:tags => {:includes => "riak"},
69
+ * :title => {"~=" => "schema"}})
70
+ */
71
+ filterByConditions: function(value, keyData, conditions){
72
+ var object = Riak.mapValuesJson(value)[0];
73
+ for(condition in conditions){
74
+ if(!Ripple.conditionMatch(condition, conditions[condition], object))
75
+ return [];
76
+ }
77
+ return [[value.bucket, value.key]];
78
+ },
79
+ /*
80
+ * Given a specific field and test, returns whether the object
81
+ * matches the condition specified by the test. Used internally by
82
+ * Ripple.filterByConditions map phases.
83
+ */
84
+ conditionMatch: function(field, test, object){
85
+ for(t in test){
86
+ switch(t){
87
+ case "==": case "eq":
88
+ if(object[field] != test[t])
89
+ return false;
90
+ break;
91
+ case "!=": case "neq":
92
+ if(object[field] == test[t])
93
+ return false;
94
+ break;
95
+ case "<": case "lt":
96
+ if(object[field] >= test[t])
97
+ return false;
98
+ break;
99
+ case "=<": case "<=": case "lte":
100
+ if(object[field] > test[t])
101
+ return false;
102
+ break;
103
+ case ">": case "gt":
104
+ if(object[field] <= test[t])
105
+ return false;
106
+ break;
107
+ case ">=": case "=>": case "gte":
108
+ if(object[field] < test[t])
109
+ return false;
110
+ break;
111
+ case "~=": case "match":
112
+ if(new RegExp(object[field],"i").test(test[t]))
113
+ return false;
114
+ break;
115
+ case "between": // Inclusive on both ends
116
+ if(object[field] < test[t][0] || object[field] > test[t][1])
117
+ return false;
118
+ break;
119
+ case "includes": // Only works with String, Array
120
+ if(object[field].indexOf(test[t]) == -1)
121
+ return false;
122
+ break;
123
+ default:
124
+ ejsLog("Invalid condition for field " + field + ": " + t + " " + test[t], "ripple-error.log");
125
+ break;
126
+ }
127
+ }
128
+ return true;
129
+ },
130
+ /*
131
+ * Returns the mapped object without modification. Useful when
132
+ * preceded by map phases that do filtering or link phases.
133
+ */
134
+ mapIdentity: function(value, kd, arg){
135
+ return [value];
136
+ },
137
+ /*
138
+ * Returns the passed values without modification. Useful as an
139
+ * intermediary before reduce phases that require the entire
140
+ * result set to be present (limits).
141
+ */
142
+ reduceIdentity: function(values, arg){
143
+ return values;
144
+ }
145
+ };
@@ -16,7 +16,7 @@ require 'rails/generators/ripple_generator'
16
16
 
17
17
  module Ripple
18
18
  module Generators
19
- class ModelGenerator < Base
19
+ class ModelGenerator < NamedBase
20
20
  desc 'Creates a ripple model'
21
21
  argument :attributes, :type => :array, :default => [], :banner => 'field:type field:type'
22
22
  class_option :parent, :type => :string, :desc => "The parent class for the generated model"
@@ -0,0 +1,42 @@
1
+ require 'riak/test_server'
2
+
3
+ module Ripple
4
+ module TestServer
5
+ extend self
6
+
7
+ # Tweak this to change how your test server is configured
8
+ def test_server_config
9
+ {
10
+ :app_config => {
11
+ :riak_kv => { :js_source_dir => Ripple.config.delete(:js_source_dir) },
12
+ :riak_core => { :web_port => Ripple.config[:port] || 8098 }
13
+ },
14
+ :bin_dir => Ripple.config.delete(:bin_dir),
15
+ :temp_dir => Rails.root + "tmp/riak_test_server"
16
+ }
17
+ end
18
+
19
+ # Prepares the subprocess Riak node for the test suite
20
+ def setup
21
+ unless @test_server
22
+ begin
23
+ _server = @test_server = Riak::TestServer.new(test_server_config)
24
+ @test_server.prepare!
25
+ @test_server.start
26
+ at_exit { _server.cleanup }
27
+ rescue => e
28
+ warn "Can't run tests with Riak::TestServer. Specify the location of your Riak installation in the config/ripple.yml #{Rails.env} environment."
29
+ warn e.inspect
30
+ @test_server = nil
31
+ end
32
+ end
33
+ end
34
+
35
+ # Clear the data after each test run
36
+ def clear
37
+ @test_server.recycle if @test_server
38
+ end
39
+ end
40
+ end
41
+
42
+ Ripple::TestServer.setup
@@ -0,0 +1,52 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require 'rails/generators/ripple_generator'
15
+
16
+ module Ripple
17
+ module Generators
18
+ class TestGenerator < Base
19
+ desc 'Generates test helpers for Ripple. Test::Unit, RSpec and Cucumber are supported.'
20
+ # Cucumber
21
+ def create_cucumber_file
22
+ if File.directory?(Rails.root + "features/support")
23
+ template 'test_server.rb', 'features/support/ripple.rb'
24
+ insert_into_file 'features/support/ripple.rb', "\n\nAfter do\n Ripple::TestServer.clear\nend", :after => "Ripple::TestServer.setup"
25
+ end
26
+ end
27
+
28
+ # RSpec
29
+ def create_rspec_file
30
+ if File.file?(Rails.root + 'spec/spec_helper.rb')
31
+ template 'test_server.rb', 'spec/support/ripple.rb'
32
+ inject_into_file 'spec/spec_helper.rb', :after => /R[Ss]pec\.configure do \|config\|/ do
33
+ "\n config.after(:each) do\n Ripple::TestServer.clear\n end\n"
34
+ end
35
+ end
36
+ end
37
+
38
+ # Test::Unit
39
+ def create_test_unit_file
40
+ if File.file?(Rails.root + 'test/test_helper.rb')
41
+ template 'test_server.rb', 'test/ripple_test_helper.rb'
42
+ inject_into_file "test/test_helper.rb", :before => "class ActiveSupport::TestCase" do
43
+ "# Setup in-memory test server for Riak\nrequire File.expand_path('../ripple_test_helper.rb', __FILE__)\n\n"
44
+ end
45
+ inject_into_class "test/test_helper.rb", ActiveSupport::TestCase do
46
+ " teardown { Ripple::TestServer.clear }\n\n"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -14,12 +14,26 @@
14
14
  require "rails/generators/named_base"
15
15
  require "rails/generators/active_model"
16
16
 
17
+ class RippleGenerator < ::Rails::Generators::Base
18
+ def create_ripple
19
+ invoke "ripple:configuration"
20
+ invoke "ripple:js"
21
+ invoke "ripple:test"
22
+ end
23
+ end
24
+
17
25
  module Ripple
18
26
  # ActiveModel generators for use in a Rails project.
19
27
  module Generators
20
28
  # @private
21
- class Base < ::Rails::Generators::NamedBase
29
+ class Base < ::Rails::Generators::Base
30
+ def self.source_root
31
+ @_ripple_source_root ||=
32
+ File.expand_path("../#{base_name}/#{generator_name}/templates", __FILE__)
33
+ end
34
+ end
22
35
 
36
+ class NamedBase < ::Rails::Generators::NamedBase
23
37
  def self.source_root
24
38
  @_ripple_source_root ||=
25
39
  File.expand_path("../#{base_name}/#{generator_name}/templates", __FILE__)
data/lib/ripple.rb CHANGED
@@ -14,6 +14,7 @@
14
14
 
15
15
  require 'riak'
16
16
  require 'active_support/all'
17
+ require 'active_support/json'
17
18
  require 'active_model'
18
19
  require 'ripple/i18n'
19
20
  require 'ripple/core_ext'
@@ -252,7 +252,7 @@ module Ripple
252
252
  when many?
253
253
  Array === value && value.all? {|d| (embeddable? && Hash === d) || klass === d }
254
254
  when one?
255
- value.nil? || (embeddable? && Hash === value) || klass === value
255
+ value.nil? || (embeddable? && Hash === value) || value.kind_of?(klass)
256
256
  end
257
257
  end
258
258
  end
@@ -32,6 +32,7 @@ module Ripple
32
32
  include Write
33
33
  include Query
34
34
  include Dirty
35
+ include ActiveModel::MassAssignmentSecurity
35
36
  end
36
37
 
37
38
  module ClassMethods
@@ -64,7 +65,7 @@ module Ripple
64
65
  # @param [Hash] attrs the attributes to assign
65
66
  def attributes=(attrs)
66
67
  raise ArgumentError, t('attribute_hash') unless Hash === attrs
67
- attrs.each do |k,v|
68
+ sanitize_for_mass_assignment(attrs).each do |k,v|
68
69
  next if k.to_sym == :key
69
70
  if respond_to?("#{k}=")
70
71
  __send__("#{k}=",v)
@@ -42,7 +42,7 @@ module Ripple
42
42
 
43
43
  private
44
44
  def attribute=(attr_name, value)
45
- attribute_will_change!(attr_name)
45
+ attribute_will_change!(attr_name) if @attributes[attr_name] != value
46
46
  super
47
47
  end
48
48
  end
@@ -12,6 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require 'active_support/core_ext/object/blank'
16
+ require 'active_support/core_ext/object/to_json'
17
+ require 'active_support/time_with_zone'
18
+
15
19
  # @private
16
20
  class Object
17
21
  def self.ripple_cast(value)
@@ -22,7 +26,7 @@ end
22
26
  # @private
23
27
  class Symbol
24
28
  def self.ripple_cast(value)
25
- return nil if value.nil?
29
+ return nil if value.blank?
26
30
  value.respond_to?(:to_s) && value.to_s.intern or raise Ripple::PropertyTypeMismatch.new(self, value)
27
31
  end
28
32
  end
@@ -30,7 +34,7 @@ end
30
34
  # @private
31
35
  class Numeric
32
36
  def self.ripple_cast(value)
33
- return nil if value.nil?
37
+ return nil if value.blank?
34
38
  raise Ripple::PropertyTypeMismatch.new(self,value) unless value.respond_to?(:to_i) && value.respond_to?(:to_f)
35
39
  float_value = value.to_f
36
40
  int_value = value.to_i
@@ -41,15 +45,15 @@ end
41
45
  # @private
42
46
  class Integer
43
47
  def self.ripple_cast(value)
44
- return nil if value.nil?
45
- value.respond_to?(:to_i) && value.to_i or raise Ripple::PropertyTypeMismatch.new(self, value)
48
+ return nil if value.nil? || (String === value && value.blank?)
49
+ !value.is_a?(Symbol) && value.respond_to?(:to_i) && value.to_i or raise Ripple::PropertyTypeMismatch.new(self, value)
46
50
  end
47
51
  end
48
52
 
49
53
  # @private
50
54
  class Float
51
55
  def self.ripple_cast(value)
52
- return nil if value.nil?
56
+ return nil if value.nil? || (String === value && value.blank?)
53
57
  value.respond_to?(:to_f) && value.to_f or raise Ripple::PropertyTypeMismatch.new(self, value)
54
58
  end
55
59
  end
@@ -97,7 +101,7 @@ class Time
97
101
  end
98
102
 
99
103
  def self.ripple_cast(value)
100
- return nil if value.nil?
104
+ return nil if value.blank?
101
105
  value.respond_to?(:to_time) && value.to_time or raise Ripple::PropertyTypeMismatch.new(self, value)
102
106
  end
103
107
  end
@@ -109,7 +113,7 @@ class Date
109
113
  end
110
114
 
111
115
  def self.ripple_cast(value)
112
- return nil if value.nil?
116
+ return nil if value.blank?
113
117
  value.respond_to?(:to_date) && value.to_date or raise Ripple::PropertyTypeMismatch.new(self, value)
114
118
  end
115
119
  end
@@ -121,7 +125,17 @@ class DateTime
121
125
  end
122
126
 
123
127
  def self.ripple_cast(value)
124
- return nil if value.nil?
128
+ return nil if value.blank?
125
129
  value.respond_to?(:to_datetime) && value.to_datetime or raise Ripple::PropertyTypeMismatch.new(self, value)
126
130
  end
127
131
  end
132
+
133
+ # @private
134
+ module ActiveSupport
135
+ class TimeWithZone
136
+ def as_json(options={})
137
+ self.utc.rfc822
138
+ end
139
+ end
140
+ end
141
+
@@ -26,7 +26,7 @@ module Ripple
26
26
  def initialize(keys, found)
27
27
  if keys.empty?
28
28
  super(t("document_not_found.no_key"))
29
- elsif keys.one?
29
+ elsif keys.size == 1
30
30
  super(t("document_not_found.one_key", :key => keys.first))
31
31
  else
32
32
  missing = keys - found.compact.map(&:key)
@@ -57,7 +57,7 @@ module Ripple
57
57
  def find(*args)
58
58
  args.flatten!
59
59
  return nil if args.empty? || args.all?(&:blank?)
60
- return find_one(args.first) if args.one?
60
+ return find_one(args.first) if args.size == 1
61
61
  args.map {|key| find_one(key) }
62
62
  end
63
63
 
@@ -118,10 +118,9 @@ module Ripple
118
118
 
119
119
  def instantiate(robject)
120
120
  klass = robject.data['_type'].constantize rescue self
121
- data = {'key' => robject.key}
122
- data.reverse_merge!(robject.data) rescue data
123
- klass.new(data).tap do |doc|
124
- doc.key = data['key']
121
+ klass.new.tap do |doc|
122
+ doc.key = robject.key
123
+ robject.data.except("_type").each {|k,v| doc.send("#{k}=", v) } if robject.data
125
124
  doc.instance_variable_set(:@new, false)
126
125
  doc.instance_variable_set(:@robject, robject)
127
126
  end
@@ -85,7 +85,7 @@ module Ripple
85
85
  def reload
86
86
  return self if new?
87
87
  robject.reload(:force => true)
88
- @attributes.merge!(@robject.data)
88
+ @robject.data.except("_type").each { |key, value| send("#{key}=", value) }
89
89
  self
90
90
  end
91
91
 
@@ -18,7 +18,9 @@ module Ripple
18
18
  # during Rails initialization.
19
19
  class Railtie < Rails::Railtie
20
20
  initializer "ripple.configure_rails_initialization" do
21
- Ripple.load_configuration Rails.root.join('config', 'ripple.yml'), [Rails.env]
21
+ if File.exist?(Rails.root + "config/ripple.yml")
22
+ Ripple.load_configuration Rails.root.join('config', 'ripple.yml'), [Rails.env]
23
+ end
22
24
  end
23
25
  end
24
26
  end
@@ -23,7 +23,7 @@ module Ripple
23
23
  # Adds the :created_at and :updated_at timestamp properties to
24
24
  # the document.
25
25
  def timestamps!
26
- property :created_at, Time, :default => proc { Time.now.utc }
26
+ property :created_at, Time, :default => proc { Time.now }
27
27
  property :updated_at, Time
28
28
  before_save :touch
29
29
  end
@@ -32,7 +32,7 @@ module Ripple
32
32
  module InstanceMethods
33
33
  # Sets the :updated_at attribute before saving the document.
34
34
  def touch
35
- self.updated_at = Time.now.utc
35
+ self.updated_at = Time.now
36
36
  end
37
37
  end
38
38
  end
data/ripple.gemspec ADDED
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{ripple}
5
+ s.version = "0.8.3"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Sean Cribbs"]
9
+ s.date = %q{2010-12-13}
10
+ s.description = %q{ripple is an object-mapper library for Riak, the distributed database by Basho. It uses ActiveModel to provide an experience that integrates well with Rails 3 applications.}
11
+ s.email = %q{sean@basho.com}
12
+ s.files = ["Gemfile", "lib/rails/generators/ripple/configuration/configuration_generator.rb", "lib/rails/generators/ripple/configuration/templates/ripple.yml", "lib/rails/generators/ripple/js/js_generator.rb", "lib/rails/generators/ripple/js/templates/js/contrib.js", "lib/rails/generators/ripple/js/templates/js/ripple.js", "lib/rails/generators/ripple/model/model_generator.rb", "lib/rails/generators/ripple/model/templates/model.rb", "lib/rails/generators/ripple/test/templates/test_server.rb", "lib/rails/generators/ripple/test/test_generator.rb", "lib/rails/generators/ripple_generator.rb", "lib/ripple/associations/embedded.rb", "lib/ripple/associations/instantiators.rb", "lib/ripple/associations/linked.rb", "lib/ripple/associations/many.rb", "lib/ripple/associations/many_embedded_proxy.rb", "lib/ripple/associations/many_linked_proxy.rb", "lib/ripple/associations/one.rb", "lib/ripple/associations/one_embedded_proxy.rb", "lib/ripple/associations/one_linked_proxy.rb", "lib/ripple/associations/proxy.rb", "lib/ripple/associations.rb", "lib/ripple/attribute_methods/dirty.rb", "lib/ripple/attribute_methods/query.rb", "lib/ripple/attribute_methods/read.rb", "lib/ripple/attribute_methods/write.rb", "lib/ripple/attribute_methods.rb", "lib/ripple/callbacks.rb", "lib/ripple/conversion.rb", "lib/ripple/core_ext/casting.rb", "lib/ripple/core_ext.rb", "lib/ripple/document/bucket_access.rb", "lib/ripple/document/finders.rb", "lib/ripple/document/key.rb", "lib/ripple/document/persistence.rb", "lib/ripple/document.rb", "lib/ripple/embedded_document/finders.rb", "lib/ripple/embedded_document/persistence.rb", "lib/ripple/embedded_document.rb", "lib/ripple/i18n.rb", "lib/ripple/inspection.rb", "lib/ripple/locale/en.yml", "lib/ripple/nested_attributes.rb", "lib/ripple/properties.rb", "lib/ripple/property_type_mismatch.rb", "lib/ripple/railtie.rb", "lib/ripple/timestamps.rb", "lib/ripple/translation.rb", "lib/ripple/validations/associated_validator.rb", "lib/ripple/validations.rb", "lib/ripple.rb", "Rakefile", "ripple.gemspec", "spec/fixtures/config.yml", "spec/integration/ripple/associations_spec.rb", "spec/integration/ripple/nested_attributes_spec.rb", "spec/integration/ripple/persistence_spec.rb", "spec/ripple/associations/many_embedded_proxy_spec.rb", "spec/ripple/associations/many_linked_proxy_spec.rb", "spec/ripple/associations/one_embedded_proxy_spec.rb", "spec/ripple/associations/one_linked_proxy_spec.rb", "spec/ripple/associations/proxy_spec.rb", "spec/ripple/associations_spec.rb", "spec/ripple/attribute_methods_spec.rb", "spec/ripple/bucket_access_spec.rb", "spec/ripple/callbacks_spec.rb", "spec/ripple/conversion_spec.rb", "spec/ripple/core_ext_spec.rb", "spec/ripple/document_spec.rb", "spec/ripple/embedded_document/finders_spec.rb", "spec/ripple/embedded_document/persistence_spec.rb", "spec/ripple/embedded_document_spec.rb", "spec/ripple/finders_spec.rb", "spec/ripple/inspection_spec.rb", "spec/ripple/key_spec.rb", "spec/ripple/persistence_spec.rb", "spec/ripple/properties_spec.rb", "spec/ripple/ripple_spec.rb", "spec/ripple/timestamps_spec.rb", "spec/ripple/validations_spec.rb", "spec/spec_helper.rb", "spec/support/associations/proxies.rb", "spec/support/mocks.rb", "spec/support/models/address.rb", "spec/support/models/box.rb", "spec/support/models/car.rb", "spec/support/models/cardboard_box.rb", "spec/support/models/clock.rb", "spec/support/models/customer.rb", "spec/support/models/driver.rb", "spec/support/models/email.rb", "spec/support/models/engine.rb", "spec/support/models/family.rb", "spec/support/models/favorite.rb", "spec/support/models/invoice.rb", "spec/support/models/late_invoice.rb", "spec/support/models/note.rb", "spec/support/models/page.rb", "spec/support/models/paid_invoice.rb", "spec/support/models/passenger.rb", "spec/support/models/seat.rb", "spec/support/models/tasks.rb", "spec/support/models/tree.rb", "spec/support/models/user.rb", "spec/support/models/wheel.rb", "spec/support/models/widget.rb", "spec/support/test_server.rb", "spec/support/test_server.yml.example"]
13
+ s.homepage = %q{http://seancribbs.github.com/ripple}
14
+ s.require_paths = ["lib"]
15
+ s.rubygems_version = %q{1.3.7}
16
+ s.summary = %q{ripple is an object-mapper library for Riak, the distributed database by Basho.}
17
+ s.test_files = ["spec/integration/ripple/associations_spec.rb", "spec/integration/ripple/nested_attributes_spec.rb", "spec/integration/ripple/persistence_spec.rb", "spec/ripple/associations/many_embedded_proxy_spec.rb", "spec/ripple/associations/many_linked_proxy_spec.rb", "spec/ripple/associations/one_embedded_proxy_spec.rb", "spec/ripple/associations/one_linked_proxy_spec.rb", "spec/ripple/associations/proxy_spec.rb", "spec/ripple/associations_spec.rb", "spec/ripple/attribute_methods_spec.rb", "spec/ripple/bucket_access_spec.rb", "spec/ripple/callbacks_spec.rb", "spec/ripple/conversion_spec.rb", "spec/ripple/core_ext_spec.rb", "spec/ripple/document_spec.rb", "spec/ripple/embedded_document/finders_spec.rb", "spec/ripple/embedded_document/persistence_spec.rb", "spec/ripple/embedded_document_spec.rb", "spec/ripple/finders_spec.rb", "spec/ripple/inspection_spec.rb", "spec/ripple/key_spec.rb", "spec/ripple/persistence_spec.rb", "spec/ripple/properties_spec.rb", "spec/ripple/ripple_spec.rb", "spec/ripple/timestamps_spec.rb", "spec/ripple/validations_spec.rb"]
18
+
19
+ if s.respond_to? :specification_version then
20
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
21
+ s.specification_version = 3
22
+
23
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
24
+ s.add_development_dependency(%q<rspec>, ["~> 2.0.0"])
25
+ s.add_runtime_dependency(%q<riak-client>, ["~> 0.8.3"])
26
+ s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.0"])
27
+ s.add_runtime_dependency(%q<activemodel>, ["~> 3.0.0"])
28
+ else
29
+ s.add_dependency(%q<rspec>, ["~> 2.0.0"])
30
+ s.add_dependency(%q<riak-client>, ["~> 0.8.3"])
31
+ s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
32
+ s.add_dependency(%q<activemodel>, ["~> 3.0.0"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<rspec>, ["~> 2.0.0"])
36
+ s.add_dependency(%q<riak-client>, ["~> 0.8.3"])
37
+ s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
38
+ s.add_dependency(%q<activemodel>, ["~> 3.0.0"])
39
+ end
40
+ end
@@ -15,7 +15,8 @@ require File.expand_path("../../../spec_helper", __FILE__)
15
15
 
16
16
  describe Ripple::Associations::OneLinkedProxy do
17
17
  require 'support/models/tasks'
18
-
18
+ require 'support/models/family'
19
+
19
20
  before :each do
20
21
  @person = Person.new {|p| p.key = "riak-user" }
21
22
  @profile = Profile.new {|t| t.key = "one" }
@@ -28,7 +29,7 @@ describe Ripple::Associations::OneLinkedProxy do
28
29
  it "should be blank before the associated document is set" do
29
30
  @person.profile.should_not be_present
30
31
  end
31
-
32
+
32
33
  it "should accept a single document" do
33
34
  lambda { @person.profile = @profile }.should_not raise_error
34
35
  end
@@ -48,8 +49,8 @@ describe Ripple::Associations::OneLinkedProxy do
48
49
  @profile.should_receive(:save).and_return(true)
49
50
  @person.profile = @profile
50
51
  end
51
-
52
- it "should link-walk to the associated document when accessing" do
52
+
53
+ it "should link-walk to the associated document when accessing" do
53
54
  @person.robject.links << @profile.robject.to_link("profile")
54
55
  @person.robject.should_receive(:walk).with(Riak::WalkSpec.new(:bucket => "profiles", :tag => "profile")).and_return([[@profile.robject]])
55
56
  @person.profile.should be_present
@@ -59,13 +60,28 @@ describe Ripple::Associations::OneLinkedProxy do
59
60
  @person.robject.links.should be_empty
60
61
  @person.profile.should be_nil
61
62
  end
62
-
63
+
63
64
  it "should replace associated document with a new one" do
64
65
  @person.profile = @profile
65
66
  @person.profile = @other_profile
66
67
  @person.profile.should == @other_profile
67
68
  end
68
69
 
70
+ it "replaces the associated document with the target of the proxy" do
71
+ @other_person = Person.new {|p| p.key = "another-riak-user" }
72
+ @other_person.profile = @other_profile
73
+
74
+ @person.profile = @other_person.profile
75
+ @person.profile.should == @other_profile
76
+ end
77
+
78
+ it "refuses assigning a proxy if its target is the wrong type" do
79
+ parent = Parent.new
80
+ parent.child = Child.new
81
+
82
+ lambda { @person.profile = parent.child }.should raise_error
83
+ end
84
+
69
85
  # it "should be able to build a new associated document" do
70
86
  # @person.profile.build
71
87
  # end
@@ -130,6 +130,13 @@ describe Ripple::AttributeMethods do
130
130
  @widget.changes.should == {"name" => ["widget", "foobar"]}
131
131
  end
132
132
 
133
+ it "should report that an attribute is changed only if its value actually changes" do
134
+ @widget.name = "widget"
135
+ @widget.changed?.should be_false
136
+ @widget.name_changed?.should be_false
137
+ @widget.changes.should be_blank
138
+ end
139
+
133
140
  it "should refresh the attribute methods when adding a new property" do
134
141
  Widget.should_receive(:undefine_attribute_methods)
135
142
  Widget.property :start_date, Date
@@ -137,7 +144,7 @@ describe Ripple::AttributeMethods do
137
144
  end
138
145
 
139
146
  it "should provide a hash representation of all of the attributes" do
140
- @widget.attributes.should == {"name" => "widget", "size" => nil, "manufactured" => false}
147
+ @widget.attributes.should == {"name" => "widget", "size" => nil, "manufactured" => false, "shipped_at" => nil}
141
148
  end
142
149
 
143
150
  it "should load attributes from mass assignment" do
@@ -177,4 +184,9 @@ describe Ripple::AttributeMethods do
177
184
  @widget = Widget.new
178
185
  lambda { @widget.attributes = nil }.should raise_error(ArgumentError)
179
186
  end
187
+
188
+ it "should protect attributes from mass assignment" do
189
+ @widget = Widget.new(:manufactured => true)
190
+ @widget.manufactured.should be_false
191
+ end
180
192
  end
@@ -35,6 +35,14 @@ describe DateTime do
35
35
  end
36
36
  end
37
37
 
38
+ describe ActiveSupport::TimeWithZone do
39
+ it "serializes to JSON in UTC, RFC 822 format" do
40
+ time = Time.utc(2010,3,16,12)
41
+ zone = ActiveSupport::TimeZone['Alaska']
42
+ ActiveSupport::TimeWithZone.new(time, zone).as_json.should == "Tue, 16 Mar 2010 12:00:00 -0000"
43
+ end
44
+ end
45
+
38
46
  describe "Boolean" do
39
47
  it "should be available to properties on documents" do
40
48
  lambda {
@@ -71,6 +71,11 @@ describe Ripple::Document::Finders do
71
71
  lambda { Box.find!("square") }.should_not raise_error(Ripple::DocumentNotFound)
72
72
  end
73
73
 
74
+ it "should raise an exception when finding an existing document that has properties we don't know about" do
75
+ @http.should_receive(:get).with(200, "/riak/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"non_existent_property":"whatever"}'})
76
+ lambda { Box.find("square") }.should raise_error
77
+ end
78
+
74
79
  it "should return the document when calling find!" do
75
80
  @http.should_receive(:get).with(200, "/riak/", "boxes", "square", {}, {}).and_return({:code => 200, :headers => {"content-type" => ["application/json"]}, :body => '{"shape":"square"}'})
76
81
  box = Box.find!("square")
@@ -54,10 +54,11 @@ describe Ripple::Document::Persistence do
54
54
  end
55
55
 
56
56
  it "should instantiate and save a new object to riak" do
57
- json = @widget.attributes.merge(:size => 10, :_type => 'Widget').to_json
57
+ json = @widget.attributes.merge(:size => 10, :shipped_at => "Sat, 01 Jan 2000 20:15:01 -0000", :_type => 'Widget').to_json
58
58
  @http.should_receive(:post).with(201, "/riak/", "widgets", an_instance_of(Hash), json, hash_including("Content-Type" => "application/json")).and_return(:code => 201, :headers => {'location' => ["/riak/widgets/new_widget"]})
59
- @widget = Widget.create(:size => 10)
59
+ @widget = Widget.create(:size => 10, :shipped_at => Time.utc(2000,"jan",1,20,15,1))
60
60
  @widget.size.should == 10
61
+ @widget.shipped_at.should == Time.utc(2000,"jan",1,20,15,1)
61
62
  @widget.should_not be_a_new_record
62
63
  end
63
64
 
@@ -72,14 +73,15 @@ describe Ripple::Document::Persistence do
72
73
  end
73
74
 
74
75
  it "should reload a saved object" do
75
- json = @widget.attributes.merge("_type" => "Widget").to_json
76
+ json = @widget.attributes.merge(:_type => "Widget").to_json
76
77
  @http.should_receive(:post).with(201, "/riak/", "widgets", an_instance_of(Hash), json, hash_including("Content-Type" => "application/json")).and_return(:code => 201, :headers => {'location' => ["/riak/widgets/new_widget"]})
77
78
  @widget.save
78
- @http.should_receive(:get).and_return(:code => 200, :headers => {'content-type' => ["application/json"]}, :body => '{"name":"spring","size":10}')
79
+ @http.should_receive(:get).and_return(:code => 200, :headers => {'content-type' => ["application/json"]}, :body => '{"name":"spring","size":10,"shipped_at":"Sat, 01 Jan 2000 20:15:01 -0000","_type":"Widget"}')
79
80
  @widget.reload
80
81
  @widget.changes.should be_blank
81
82
  @widget.name.should == "spring"
82
83
  @widget.size.should == 10
84
+ @widget.shipped_at.should == Time.utc(2000,"jan",1,20,15,1)
83
85
  end
84
86
 
85
87
  it "should destroy a saved object" do
@@ -145,13 +145,18 @@ describe Ripple::Property do
145
145
  end
146
146
  end
147
147
 
148
- [0.0, "0", " 000", ""].each do |v|
148
+ [0.0, "0", " 000"].each do |v|
149
149
  it "should cast #{v.inspect} to 0" do
150
150
  @prop.type_cast(v).should == 0
151
151
  end
152
152
  end
153
153
 
154
- [true, false, [], ["something else"]].each do |v|
154
+ it "should not cast the blank string" do
155
+ @prop.type_cast("").should be_nil
156
+ @prop.type_cast(" ").should be_nil
157
+ end
158
+
159
+ [true, false, [], {}, :symbol, ["something else"]].each do |v|
155
160
  it "should raise an error casting #{v.inspect}" do
156
161
  lambda { @prop.type_cast(v) }.should raise_error(Ripple::PropertyTypeMismatch)
157
162
  end
@@ -163,7 +168,7 @@ describe Ripple::Property do
163
168
  @prop = Ripple::Property.new(:foo, Float)
164
169
  end
165
170
 
166
- [0, "0", "0.0", " 0.0", ""].each do |v|
171
+ [0, "0", "0.0", " 0.0"].each do |v|
167
172
  it "should cast #{v.inspect} to 0.0" do
168
173
  @prop.type_cast(v).should == 0.0
169
174
  end
@@ -175,6 +180,11 @@ describe Ripple::Property do
175
180
  end
176
181
  end
177
182
 
183
+ it "should not cast the blank string" do
184
+ @prop.type_cast("").should be_nil
185
+ @prop.type_cast(" ").should be_nil
186
+ end
187
+
178
188
  [true, false, :symbol, [], {}].each do |v|
179
189
  it "should raise an error casting #{v.inspect}" do
180
190
  lambda { @prop.type_cast(v) }.should raise_error(Ripple::PropertyTypeMismatch)
@@ -198,6 +208,11 @@ describe Ripple::Property do
198
208
  @prop.type_cast(v).should be_kind_of(Float)
199
209
  end
200
210
  end
211
+
212
+ it "should not cast the blank string" do
213
+ @prop.type_cast("").should be_nil
214
+ @prop.type_cast(" ").should be_nil
215
+ end
201
216
  end
202
217
 
203
218
  describe "when type is a Time type" do
@@ -210,6 +225,13 @@ describe Ripple::Property do
210
225
  @prop.type_cast(v).should == Time.utc(2010,03,16,12)
211
226
  end
212
227
  end
228
+
229
+ it "should not cast blank types" do
230
+ @prop.type_cast([]).should be_nil
231
+ @prop.type_cast({}).should be_nil
232
+ @prop.type_cast("").should be_nil
233
+ @prop.type_cast(" ").should be_nil
234
+ end
213
235
  end
214
236
 
215
237
  describe "when type is a Date type" do
@@ -222,6 +244,13 @@ describe Ripple::Property do
222
244
  @prop.type_cast(v).should == Date.civil(2010,3,16)
223
245
  end
224
246
  end
247
+
248
+ it "should not cast blank types" do
249
+ @prop.type_cast([]).should be_nil
250
+ @prop.type_cast({}).should be_nil
251
+ @prop.type_cast("").should be_nil
252
+ @prop.type_cast(" ").should be_nil
253
+ end
225
254
  end
226
255
  end
227
256
  end
@@ -17,6 +17,9 @@ class Widget
17
17
  property :size, Integer
18
18
  property :name, String, :default => "widget"
19
19
  property :manufactured, Boolean, :default => false
20
+ property :shipped_at, Time
21
+
22
+ attr_protected :manufactured
20
23
  end
21
24
 
22
25
  class Cog < Widget
@@ -0,0 +1,2 @@
1
+ bin_dir: /Users/sean/Development/riak/rel/riak/bin
2
+ temp_dir: /Users/sean/Development/ripple/.riaktest
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 8
8
- - 2
9
- version: 0.8.2
8
+ - 3
9
+ version: 0.8.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Sean Cribbs
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-10-22 00:00:00 -04:00
17
+ date: 2010-12-13 00:00:00 -06:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -43,8 +43,8 @@ dependencies:
43
43
  segments:
44
44
  - 0
45
45
  - 8
46
- - 2
47
- version: 0.8.2
46
+ - 3
47
+ version: 0.8.3
48
48
  type: :runtime
49
49
  version_requirements: *id002
50
50
  - !ruby/object:Gem::Dependency
@@ -78,7 +78,7 @@ dependencies:
78
78
  type: :runtime
79
79
  version_requirements: *id004
80
80
  description: ripple is an object-mapper library for Riak, the distributed database by Basho. It uses ActiveModel to provide an experience that integrates well with Rails 3 applications.
81
- email: seancribbs@gmail.com
81
+ email: sean@basho.com
82
82
  executables: []
83
83
 
84
84
  extensions: []
@@ -86,8 +86,16 @@ extensions: []
86
86
  extra_rdoc_files: []
87
87
 
88
88
  files:
89
+ - Gemfile
90
+ - lib/rails/generators/ripple/configuration/configuration_generator.rb
91
+ - lib/rails/generators/ripple/configuration/templates/ripple.yml
92
+ - lib/rails/generators/ripple/js/js_generator.rb
93
+ - lib/rails/generators/ripple/js/templates/js/contrib.js
94
+ - lib/rails/generators/ripple/js/templates/js/ripple.js
89
95
  - lib/rails/generators/ripple/model/model_generator.rb
90
96
  - lib/rails/generators/ripple/model/templates/model.rb
97
+ - lib/rails/generators/ripple/test/templates/test_server.rb
98
+ - lib/rails/generators/ripple/test/test_generator.rb
91
99
  - lib/rails/generators/ripple_generator.rb
92
100
  - lib/ripple/associations/embedded.rb
93
101
  - lib/ripple/associations/instantiators.rb
@@ -130,6 +138,7 @@ files:
130
138
  - lib/ripple/validations.rb
131
139
  - lib/ripple.rb
132
140
  - Rakefile
141
+ - ripple.gemspec
133
142
  - spec/fixtures/config.yml
134
143
  - spec/integration/ripple/associations_spec.rb
135
144
  - spec/integration/ripple/nested_attributes_spec.rb
@@ -184,6 +193,7 @@ files:
184
193
  - spec/support/models/wheel.rb
185
194
  - spec/support/models/widget.rb
186
195
  - spec/support/test_server.rb
196
+ - spec/support/test_server.yml.example
187
197
  has_rdoc: true
188
198
  homepage: http://seancribbs.github.com/ripple
189
199
  licenses: []
@@ -243,30 +253,3 @@ test_files:
243
253
  - spec/ripple/ripple_spec.rb
244
254
  - spec/ripple/timestamps_spec.rb
245
255
  - spec/ripple/validations_spec.rb
246
- - spec/spec_helper.rb
247
- - spec/support/associations/proxies.rb
248
- - spec/support/mocks.rb
249
- - spec/support/models/address.rb
250
- - spec/support/models/box.rb
251
- - spec/support/models/car.rb
252
- - spec/support/models/cardboard_box.rb
253
- - spec/support/models/clock.rb
254
- - spec/support/models/customer.rb
255
- - spec/support/models/driver.rb
256
- - spec/support/models/email.rb
257
- - spec/support/models/engine.rb
258
- - spec/support/models/family.rb
259
- - spec/support/models/favorite.rb
260
- - spec/support/models/invoice.rb
261
- - spec/support/models/late_invoice.rb
262
- - spec/support/models/note.rb
263
- - spec/support/models/page.rb
264
- - spec/support/models/paid_invoice.rb
265
- - spec/support/models/passenger.rb
266
- - spec/support/models/seat.rb
267
- - spec/support/models/tasks.rb
268
- - spec/support/models/tree.rb
269
- - spec/support/models/user.rb
270
- - spec/support/models/wheel.rb
271
- - spec/support/models/widget.rb
272
- - spec/support/test_server.rb