opium 1.0.0.beta

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