opium 1.0.0.beta

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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +24 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +11 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +71 -0
  9. data/Rakefile +10 -0
  10. data/lib/generators/opium/config_generator.rb +15 -0
  11. data/lib/generators/opium/model_generator.rb +33 -0
  12. data/lib/generators/opium/templates/config.yml +27 -0
  13. data/lib/generators/opium/templates/model.rb +10 -0
  14. data/lib/opium/config.rb +44 -0
  15. data/lib/opium/extensions/array.rb +10 -0
  16. data/lib/opium/extensions/boolean.rb +13 -0
  17. data/lib/opium/extensions/date.rb +18 -0
  18. data/lib/opium/extensions/date_time.rb +18 -0
  19. data/lib/opium/extensions/false_class.rb +7 -0
  20. data/lib/opium/extensions/float.rb +13 -0
  21. data/lib/opium/extensions/geo_point.rb +37 -0
  22. data/lib/opium/extensions/hash.rb +43 -0
  23. data/lib/opium/extensions/integer.rb +13 -0
  24. data/lib/opium/extensions/numeric.rb +7 -0
  25. data/lib/opium/extensions/object.rb +15 -0
  26. data/lib/opium/extensions/pointer.rb +20 -0
  27. data/lib/opium/extensions/regexp.rb +12 -0
  28. data/lib/opium/extensions/string.rb +20 -0
  29. data/lib/opium/extensions/time.rb +19 -0
  30. data/lib/opium/extensions/true_class.rb +7 -0
  31. data/lib/opium/extensions.rb +16 -0
  32. data/lib/opium/model/attributable.rb +37 -0
  33. data/lib/opium/model/callbacks.rb +38 -0
  34. data/lib/opium/model/connectable.rb +155 -0
  35. data/lib/opium/model/criteria.rb +123 -0
  36. data/lib/opium/model/dirty.rb +35 -0
  37. data/lib/opium/model/field.rb +31 -0
  38. data/lib/opium/model/fieldable.rb +57 -0
  39. data/lib/opium/model/findable.rb +20 -0
  40. data/lib/opium/model/kaminari/queryable.rb +46 -0
  41. data/lib/opium/model/kaminari/scopable.rb +15 -0
  42. data/lib/opium/model/kaminari.rb +4 -0
  43. data/lib/opium/model/persistable.rb +153 -0
  44. data/lib/opium/model/queryable.rb +150 -0
  45. data/lib/opium/model/scopable.rb +58 -0
  46. data/lib/opium/model/serialization.rb +13 -0
  47. data/lib/opium/model.rb +47 -0
  48. data/lib/opium/railtie.rb +14 -0
  49. data/lib/opium/user.rb +44 -0
  50. data/lib/opium/version.rb +3 -0
  51. data/lib/opium.rb +9 -0
  52. data/opium.gemspec +40 -0
  53. data/spec/opium/config/opium.yml +5 -0
  54. data/spec/opium/config_spec.rb +56 -0
  55. data/spec/opium/extensions/array_spec.rb +34 -0
  56. data/spec/opium/extensions/boolean_spec.rb +28 -0
  57. data/spec/opium/extensions/date_spec.rb +55 -0
  58. data/spec/opium/extensions/date_time_spec.rb +55 -0
  59. data/spec/opium/extensions/float_spec.rb +42 -0
  60. data/spec/opium/extensions/geo_point_spec.rb +55 -0
  61. data/spec/opium/extensions/hash_spec.rb +76 -0
  62. data/spec/opium/extensions/integer_spec.rb +42 -0
  63. data/spec/opium/extensions/object_spec.rb +24 -0
  64. data/spec/opium/extensions/pointer_spec.rb +28 -0
  65. data/spec/opium/extensions/regexp_spec.rb +23 -0
  66. data/spec/opium/extensions/string_spec.rb +65 -0
  67. data/spec/opium/extensions/time_spec.rb +55 -0
  68. data/spec/opium/model/attributable_spec.rb +45 -0
  69. data/spec/opium/model/callbacks_spec.rb +59 -0
  70. data/spec/opium/model/connectable_spec.rb +218 -0
  71. data/spec/opium/model/criteria_spec.rb +285 -0
  72. data/spec/opium/model/dirty_spec.rb +39 -0
  73. data/spec/opium/model/fieldable_spec.rb +133 -0
  74. data/spec/opium/model/findable_spec.rb +57 -0
  75. data/spec/opium/model/kaminari/queryable_spec.rb +22 -0
  76. data/spec/opium/model/kaminari/scopable_spec.rb +20 -0
  77. data/spec/opium/model/kaminari_spec.rb +104 -0
  78. data/spec/opium/model/persistable_spec.rb +367 -0
  79. data/spec/opium/model/queryable_spec.rb +338 -0
  80. data/spec/opium/model/scopable_spec.rb +115 -0
  81. data/spec/opium/model/serialization_spec.rb +51 -0
  82. data/spec/opium/model_spec.rb +49 -0
  83. data/spec/opium/user_spec.rb +195 -0
  84. data/spec/opium_spec.rb +5 -0
  85. data/spec/spec_helper.rb +25 -0
  86. metadata +400 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7949dfc491925078b8d64f4b093524530d183608
4
+ data.tar.gz: d9949725cb81e568d62bd46c1be3fb28af0c2b08
5
+ SHA512:
6
+ metadata.gz: 012d1e38fab68ab65636e981b5c06861dd6acdee6e4d0d025ee9ac72b6a9c7216e3fd15e4a9b999adff35fcb706253520f78d5fda386430240ad31511d67b6a3
7
+ data.tar.gz: 496fd7162e76f218f08caaa37cb300bb3662843577c4c77f4cb31ab98e00f8f34606cab80540ba482c9bda15fc0069c429284f584c5f98bd7db0cb36e50ed462
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .DS_Store
24
+ local/
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ cache: bundler
3
+
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.2
8
+ - 2.2.0
9
+
10
+ script: 'bundle exec rake'
11
+
12
+ notifications:
13
+ email:
14
+ recipients:
15
+ - joshua.bowers+code@gmail.com
16
+ on_failure: change
17
+ on_success: never
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in opium.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,11 @@
1
+ guard 'rspec', cmd: "bundle exec rspec" do
2
+ # watch /lib/ files
3
+ watch(%r{^lib/(.+).rb$}) do |m|
4
+ "spec/#{m[1]}_spec.rb"
5
+ end
6
+
7
+ # watch /spec/ files
8
+ watch(%r{^spec/(.+).rb$}) do |m|
9
+ "spec/#{m[1]}.rb"
10
+ end
11
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Joshua Bowers
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Opium
2
+
3
+ Provides an intuitive, Mongoid-inspired mapping layer between your application's object space and Parse.
4
+
5
+ [![Build Status](https://travis-ci.org/joshuabowers/opium.svg?branch=master)](https://travis-ci.org/joshuabowers/opium)
6
+ [![Coverage Status](https://img.shields.io/coveralls/joshuabowers/opium.svg)](https://coveralls.io/r/joshuabowers/opium)
7
+ [![Code Climate](https://codeclimate.com/github/joshuabowers/opium/badges/gpa.svg)](https://codeclimate.com/github/joshuabowers/opium)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'opium'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install opium
22
+
23
+ ## Usage
24
+
25
+ ### Within Rails
26
+
27
+ Opium will automatically establish itself as the default ORM for Rails.
28
+
29
+ #### ORM Configuration
30
+
31
+ Create a config file to communicate with your Parse database by running the config generator:
32
+
33
+ ```bash
34
+ $ rails g opium:config
35
+ ```
36
+
37
+ See the generated file at `config/opium.yml` for more details
38
+
39
+ #### Model Generator
40
+
41
+ A generator exists for creating new models; this should be invoked whenever `rails g model` gets invoked.
42
+
43
+ ```bash
44
+ $ rails g model game title:string price:float
45
+ ```
46
+
47
+ ### Specifying a model
48
+
49
+ Models are defined by mixing in `Opium::Model` into a new class. Class names should match the names of the
50
+ classes defined within Parse. You can define fields on your model which mirror the columns within a Parse
51
+ class.
52
+
53
+ ```ruby
54
+ class Game
55
+ include Opium::Model
56
+
57
+ field :title, type: String
58
+ field :price, type: Float
59
+ end
60
+ ```
61
+
62
+ All models automatically come with three fields: *:id*, *:created_at*, and *:updated_at*. Field names are
63
+ converted from a native ruby snake_case naming convention to a Parse lowerCamel convention.
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it ( https://github.com/[my-github-username]/opium/fork )
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rspec/core/rake_task'
2
+ require "bundler/gem_tasks"
3
+
4
+ # Default directory to look in is `/specs`
5
+ # Run with `rake spec`
6
+ RSpec::Core::RakeTask.new(:spec) do |task|
7
+ task.rspec_opts = %w[--color --format documentation]
8
+ end
9
+
10
+ task default: :spec
@@ -0,0 +1,15 @@
1
+ require 'rails/generators'
2
+
3
+ module Opium
4
+ module Generators
5
+ class ConfigGenerator < ::Rails::Generators::Base
6
+ source_root File.expand_path( "../templates/", __FILE__ )
7
+
8
+ desc "Creates an Opium configuration file at config/opium.yml"
9
+
10
+ def create_config_file
11
+ copy_file 'config.yml', File.join( 'config', 'opium.yml' )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ require 'rails/generators/named_base'
2
+
3
+ module Rails
4
+ module Generators
5
+ class GeneratedAttribute
6
+ def type_class
7
+ type.to_s.camelcase
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ module Opium
14
+ module Generators
15
+ class ModelGenerator < ::Rails::Generators::NamedBase
16
+ source_root File.expand_path( "../templates/", __FILE__ )
17
+
18
+ desc "Creates an Opium model"
19
+
20
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
21
+
22
+ check_class_collision
23
+
24
+ class_option :parent, type: :string, desc: "The parent model for the generated model when using STI"
25
+
26
+ def create_model_file
27
+ template "model.rb", File.join( "app/models", class_path, "#{file_name}.rb" )
28
+ end
29
+
30
+ hook_for :test_framework
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ development:
2
+ # The Application ID for your Parse app. Mandatory
3
+ app_id: PARSE-APP-ID
4
+
5
+ # The REST API key for your Parse app. Mandatory
6
+ api_key: PARSE-API-KEY
7
+
8
+ # Your Parse app's master key setting. You may omit this,
9
+ # but you will be unable to edit Users except by ACL rules
10
+ master_key: PARSE-MASTER-KEY
11
+
12
+ # Any communications done with parse will either be (true) displayed or (false) silenced
13
+ log_network_responses: true
14
+
15
+ test:
16
+ app_id: PARSE-TEST-APP-ID
17
+ api_key: PARSE-TEST-API-KEY
18
+ master_key: PARSE-TEST-MASTER-KEY
19
+ log_network_responses: true
20
+
21
+ # You could hardcode the values for the production Parse app here, but it is suggested
22
+ # you set these through the suggested environment variables.
23
+ production:
24
+ app_id: <%= ENV['PARSE_APP_ID'] %>
25
+ api_key: <%= ENV['PARSE_API_KEY'] %>
26
+ master_key: <%= ENV['PARSE_MASTER_KEY'] %>
27
+ log_network_responses: false
@@ -0,0 +1,10 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %><%= " < #{options[:parent].classify}" if options[:parent] %>
3
+ <% unless options[:parent] -%>
4
+ include Opium::Model
5
+ <% end -%>
6
+ <% attributes.reject {|attr| attr.reference?}.each do |attribute| -%>
7
+ field :<%= attribute.name %><%= ", type: #{ attribute.type_class }" if attribute.type_class %>
8
+ <% end -%>
9
+ end
10
+ <% end -%>
@@ -0,0 +1,44 @@
1
+ module Opium
2
+ extend self
3
+
4
+ def configure
5
+ yield config
6
+ end
7
+
8
+ def config
9
+ @config ||= Opium::Config.new
10
+ end
11
+
12
+ def load!( path, environment = nil )
13
+ settings = load_yaml( path, environment )
14
+ configure do |config|
15
+ settings.each do |key, value|
16
+ config.send("#{key}=", value)
17
+ end
18
+ end
19
+ end
20
+
21
+ def reset
22
+ @config = nil
23
+ end
24
+
25
+ private
26
+
27
+ def load_yaml( path, environment = nil )
28
+ env = environment ? environment.to_s : env_name
29
+ YAML.load(ERB.new(File.new(path).read).result)[env]
30
+ end
31
+
32
+ def env_name
33
+ defined?( Rails ) ? Rails.env : ( ENV["RACK_ENV"] || ENV["OPIUM_ENV"] || raise( "Could not determine environment" ) )
34
+ end
35
+
36
+ class Config
37
+ include ActiveSupport::Configurable
38
+
39
+ config_accessor( :app_id ) { 'PARSE_APP_ID' }
40
+ config_accessor( :api_key ) { 'PARSE_API_KEY' }
41
+ config_accessor( :master_key ){ 'PARSE_MASTER_KEY' }
42
+ config_accessor( :log_network_responses ) { false }
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ class Array
2
+ def to_parse
3
+ map {|value| value.to_parse}
4
+ end
5
+
6
+ def to_geo_point
7
+ return GeoPoint.new(self) if self.size == 2
8
+ fail ArgumentError, %(invalid value for GeoPoint: "#{ self.inspect }"), caller
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module Boolean
2
+ extend ActiveSupport::Concern
3
+
4
+ module ClassMethods
5
+ def to_ruby(object)
6
+ object.to_bool if object.respond_to?( :to_bool )
7
+ end
8
+
9
+ alias_method :to_parse, :to_ruby
10
+ end
11
+
12
+ extend ClassMethods
13
+ end
@@ -0,0 +1,18 @@
1
+ class Date
2
+ def to_parse
3
+ {
4
+ '__type' => 'Date',
5
+ 'iso' => self.iso8601
6
+ }
7
+ end
8
+
9
+ class << self
10
+ def to_ruby(object)
11
+ object.to_date if object
12
+ end
13
+
14
+ def to_parse(object)
15
+ object.to_date.to_parse if object
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ class DateTime < Date
2
+ def to_parse
3
+ {
4
+ '__type' => 'Date',
5
+ 'iso' => self.iso8601
6
+ }
7
+ end
8
+
9
+ class << self
10
+ def to_ruby(object)
11
+ object.to_datetime if object
12
+ end
13
+
14
+ def to_parse(object)
15
+ object.to_datetime.to_parse if object
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ class FalseClass
2
+ include Boolean
3
+
4
+ def to_bool
5
+ self
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ class Float
2
+ class << self
3
+ def to_ruby(object)
4
+ if object.is_a? Symbol
5
+ object.to_s.to_f
6
+ elsif object
7
+ object.to_f
8
+ end
9
+ end
10
+
11
+ alias_method :to_parse, :to_ruby
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ class GeoPoint
2
+ def initialize( value )
3
+ self.latitude, self.longitude = *
4
+ case value
5
+ when Hash
6
+ [value[:latitude] || value['latitude'], value[:longitude] || value['longitude']]
7
+ when Array
8
+ [value.first, value.last]
9
+ else
10
+ raise ArgumentError.new( "invalid value for GeoPoint: \"#{value}\"" )
11
+ end
12
+ end
13
+
14
+ attr_accessor :latitude, :longitude
15
+
16
+ def to_geo_point
17
+ self
18
+ end
19
+
20
+ def to_parse
21
+ { "__type" => "GeoPoint", "latitude" => self.latitude, "longitude" => self.longitude }
22
+ end
23
+
24
+ def to_s
25
+ "#{self.latitude},#{self.longitude}"
26
+ end
27
+
28
+ class << self
29
+ def to_ruby(object)
30
+ object.to_geo_point unless object.nil?
31
+ end
32
+
33
+ def to_parse(object)
34
+ object.to_geo_point.to_parse
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,43 @@
1
+ class Hash
2
+ def to_geo_point
3
+ return GeoPoint.new(self) if [:latitude, :longitude].all? {|required| self.key? required}
4
+ raise ArgumentError.new( "invalid value for GeoPoint: \"#{self}\"" )
5
+ end
6
+
7
+ def to_datetime
8
+ retrieve_iso_key.to_datetime
9
+ end
10
+
11
+ def to_date
12
+ retrieve_iso_key.to_date
13
+ end
14
+
15
+ def to_time
16
+ retrieve_iso_key.to_time
17
+ end
18
+
19
+ def to_parse
20
+ Hash[ *self.flat_map {|key, value| [key, value.to_parse]} ]
21
+ end
22
+
23
+ private
24
+
25
+ def retrieve_iso_key
26
+ validates_keys_present( '__type', 'iso' )
27
+ validate_key_equals( '__type', 'Date' )
28
+ value_for_indifferent_key('iso')
29
+ end
30
+
31
+ def validates_keys_present(*expected_keys)
32
+ result = expected_keys - self.keys.map(&:to_s)
33
+ raise ArgumentError, "expected key(s): #{result}" unless result.empty?
34
+ end
35
+
36
+ def validate_key_equals( key, value )
37
+ raise ArgumentError, "conversion to Date/Time expectes a #{key} of #{value}" unless value_for_indifferent_key( key ) == value
38
+ end
39
+
40
+ def value_for_indifferent_key( key )
41
+ self[key] || self[key.to_sym]
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ class Integer
2
+ class << self
3
+ def to_ruby(object)
4
+ if object.is_a? Symbol
5
+ object.to_s.to_i
6
+ elsif object
7
+ object.to_i
8
+ end
9
+ end
10
+
11
+ alias_method :to_parse, :to_ruby
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ class Numeric
2
+ def to_bool
3
+ return true if self == 1
4
+ return false if self == 0
5
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ class Object
2
+ def to_ruby
3
+ self
4
+ end
5
+
6
+ alias_method :to_parse, :to_ruby
7
+
8
+ class << self
9
+ def to_ruby(other)
10
+ other
11
+ end
12
+
13
+ alias_method :to_parse, :to_ruby
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ module Opium
2
+ class Pointer
3
+ def initialize( attributes = {} )
4
+ self.class_name = attributes[:class_name] || attributes[:model_name] || (attributes[:class] || attributes[:model]).model_name
5
+ self.id = attributes[:id]
6
+ end
7
+
8
+ attr_reader :class_name, :id
9
+ alias_method :model_name, :class_name
10
+
11
+ def to_parse
12
+ { __type: 'Pointer', className: class_name, objectId: id }.with_indifferent_access
13
+ end
14
+
15
+ private
16
+
17
+ attr_writer :class_name, :id
18
+ alias_method :model_name=, :class_name=
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ class Regexp
2
+ def to_parse
3
+ { '$regex' => self.source }.tap do |h|
4
+ ops = ''
5
+ { IGNORECASE => 'i', MULTILINE => 'm', EXTENDED => 'x' }.each do |option, value|
6
+ ops += value unless ( self.options & option ) == 0
7
+ end
8
+
9
+ h['$options'] = ops unless ops.blank?
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ class ::String
2
+ def to_bool
3
+ return true if self == true || self =~ (/^(true|t|yes|y|1)$/i)
4
+ return false if self == false || self.blank? || self =~ (/^(false|f|no|n|0)$/i)
5
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
6
+ end
7
+
8
+ def to_geo_point
9
+ return GeoPoint.new( self.split(',').map {|c| c.to_f} ) if self =~ /^[+-]?\d+(\.\d+)?\s*,\s*[+-]?\d+(\.\d+)?$/
10
+ raise ArgumentError.new("invalid value for GeoPoint: \"#{self}\"")
11
+ end
12
+
13
+ class << self
14
+ def to_ruby(object)
15
+ object.to_s if object
16
+ end
17
+
18
+ alias_method :to_parse, :to_ruby
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ class Time
2
+ def to_parse
3
+ {
4
+ '__type' => 'Date',
5
+ 'iso' => self.iso8601
6
+ }
7
+ end
8
+
9
+ class << self
10
+ def to_ruby(object)
11
+ object = object.utc if object.respond_to? :utc
12
+ object.to_time if object
13
+ end
14
+
15
+ def to_parse(object)
16
+ object.to_time.to_parse if object
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ class TrueClass
2
+ include Boolean
3
+
4
+ def to_bool
5
+ self
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ require 'opium/extensions/object'
2
+ require 'opium/extensions/string'
3
+ require 'opium/extensions/numeric'
4
+ require 'opium/extensions/float'
5
+ require 'opium/extensions/integer'
6
+ require 'opium/extensions/boolean'
7
+ require 'opium/extensions/true_class'
8
+ require 'opium/extensions/false_class'
9
+ require 'opium/extensions/geo_point'
10
+ require 'opium/extensions/hash'
11
+ require 'opium/extensions/array'
12
+ require 'opium/extensions/date_time'
13
+ require 'opium/extensions/date'
14
+ require 'opium/extensions/time'
15
+ require 'opium/extensions/regexp'
16
+ require 'opium/extensions/pointer'
@@ -0,0 +1,37 @@
1
+ module Opium
2
+ module Model
3
+ module Attributable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include ActiveModel::ForbiddenAttributesProtection
8
+ end
9
+
10
+ def attributes
11
+ @attributes ||= self.class.default_attributes
12
+ end
13
+
14
+ def attributes=(values)
15
+ sanitize_for_mass_assignment( rubyize_field_names( values ) ).each do |k, v|
16
+ field_info = self.class.fields[k]
17
+ if field_info.present?
18
+ send( "#{k}=", v )
19
+ else
20
+ attributes[k] = v
21
+ end
22
+ end
23
+ end
24
+
25
+ def attributes_to_parse( options = {} )
26
+ options[:except] ||= self.class.fields.values.select {|f| f.readonly? }.map {|f| f.name} if options[:not_readonly]
27
+ Hash[*self.as_json( options ).map {|k, v| [self.class.fields[k].name_to_parse, v.to_parse]}.flatten]
28
+ end
29
+
30
+ private
31
+
32
+ def rubyize_field_names( hash )
33
+ Hash[*hash.map {|k, v| [self.class.ruby_canonical_field_names[k] || k, v]}.flatten]
34
+ end
35
+ end
36
+ end
37
+ end