ripple 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
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