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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +24 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +10 -0
- data/lib/generators/opium/config_generator.rb +15 -0
- data/lib/generators/opium/model_generator.rb +33 -0
- data/lib/generators/opium/templates/config.yml +27 -0
- data/lib/generators/opium/templates/model.rb +10 -0
- data/lib/opium/config.rb +44 -0
- data/lib/opium/extensions/array.rb +10 -0
- data/lib/opium/extensions/boolean.rb +13 -0
- data/lib/opium/extensions/date.rb +18 -0
- data/lib/opium/extensions/date_time.rb +18 -0
- data/lib/opium/extensions/false_class.rb +7 -0
- data/lib/opium/extensions/float.rb +13 -0
- data/lib/opium/extensions/geo_point.rb +37 -0
- data/lib/opium/extensions/hash.rb +43 -0
- data/lib/opium/extensions/integer.rb +13 -0
- data/lib/opium/extensions/numeric.rb +7 -0
- data/lib/opium/extensions/object.rb +15 -0
- data/lib/opium/extensions/pointer.rb +20 -0
- data/lib/opium/extensions/regexp.rb +12 -0
- data/lib/opium/extensions/string.rb +20 -0
- data/lib/opium/extensions/time.rb +19 -0
- data/lib/opium/extensions/true_class.rb +7 -0
- data/lib/opium/extensions.rb +16 -0
- data/lib/opium/model/attributable.rb +37 -0
- data/lib/opium/model/callbacks.rb +38 -0
- data/lib/opium/model/connectable.rb +155 -0
- data/lib/opium/model/criteria.rb +123 -0
- data/lib/opium/model/dirty.rb +35 -0
- data/lib/opium/model/field.rb +31 -0
- data/lib/opium/model/fieldable.rb +57 -0
- data/lib/opium/model/findable.rb +20 -0
- data/lib/opium/model/kaminari/queryable.rb +46 -0
- data/lib/opium/model/kaminari/scopable.rb +15 -0
- data/lib/opium/model/kaminari.rb +4 -0
- data/lib/opium/model/persistable.rb +153 -0
- data/lib/opium/model/queryable.rb +150 -0
- data/lib/opium/model/scopable.rb +58 -0
- data/lib/opium/model/serialization.rb +13 -0
- data/lib/opium/model.rb +47 -0
- data/lib/opium/railtie.rb +14 -0
- data/lib/opium/user.rb +44 -0
- data/lib/opium/version.rb +3 -0
- data/lib/opium.rb +9 -0
- data/opium.gemspec +40 -0
- data/spec/opium/config/opium.yml +5 -0
- data/spec/opium/config_spec.rb +56 -0
- data/spec/opium/extensions/array_spec.rb +34 -0
- data/spec/opium/extensions/boolean_spec.rb +28 -0
- data/spec/opium/extensions/date_spec.rb +55 -0
- data/spec/opium/extensions/date_time_spec.rb +55 -0
- data/spec/opium/extensions/float_spec.rb +42 -0
- data/spec/opium/extensions/geo_point_spec.rb +55 -0
- data/spec/opium/extensions/hash_spec.rb +76 -0
- data/spec/opium/extensions/integer_spec.rb +42 -0
- data/spec/opium/extensions/object_spec.rb +24 -0
- data/spec/opium/extensions/pointer_spec.rb +28 -0
- data/spec/opium/extensions/regexp_spec.rb +23 -0
- data/spec/opium/extensions/string_spec.rb +65 -0
- data/spec/opium/extensions/time_spec.rb +55 -0
- data/spec/opium/model/attributable_spec.rb +45 -0
- data/spec/opium/model/callbacks_spec.rb +59 -0
- data/spec/opium/model/connectable_spec.rb +218 -0
- data/spec/opium/model/criteria_spec.rb +285 -0
- data/spec/opium/model/dirty_spec.rb +39 -0
- data/spec/opium/model/fieldable_spec.rb +133 -0
- data/spec/opium/model/findable_spec.rb +57 -0
- data/spec/opium/model/kaminari/queryable_spec.rb +22 -0
- data/spec/opium/model/kaminari/scopable_spec.rb +20 -0
- data/spec/opium/model/kaminari_spec.rb +104 -0
- data/spec/opium/model/persistable_spec.rb +367 -0
- data/spec/opium/model/queryable_spec.rb +338 -0
- data/spec/opium/model/scopable_spec.rb +115 -0
- data/spec/opium/model/serialization_spec.rb +51 -0
- data/spec/opium/model_spec.rb +49 -0
- data/spec/opium/user_spec.rb +195 -0
- data/spec/opium_spec.rb +5 -0
- data/spec/spec_helper.rb +25 -0
- metadata +400 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Callbacks
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
CALLBACKS = %w[before after around].map {|event| %w[save create update destroy].map {|action| :"#{event}_#{action}"}}.flatten +
|
7
|
+
%w[initialize find touch].map {|action| :"after_#{action}"} +
|
8
|
+
%w[before after].map {|event| :"#{event}_validation"}
|
9
|
+
|
10
|
+
included do
|
11
|
+
extend ActiveModel::Callbacks
|
12
|
+
include ActiveModel::Validations::Callbacks
|
13
|
+
|
14
|
+
define_model_callbacks :initialize, :find, :touch, only: :after
|
15
|
+
define_model_callbacks :save, :create, :update, :destroy
|
16
|
+
|
17
|
+
wrap_callbacks_around :save, :destroy, :touch, :find
|
18
|
+
wrap_callbacks_around :initialize, :create, :update, private: true
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def wrap_callbacks_around( *methods )
|
23
|
+
options = methods.last.is_a?(::Hash) ? methods.pop : {}
|
24
|
+
methods.each do |method|
|
25
|
+
class_eval do
|
26
|
+
define_method method do |*args|
|
27
|
+
run_callbacks( method ) do
|
28
|
+
super( *args )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
send( :private, method ) if options[:private]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
|
4
|
+
module Opium
|
5
|
+
module Model
|
6
|
+
module Connectable
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
end
|
11
|
+
|
12
|
+
class ParseError < StandardError
|
13
|
+
def initialize( code, error )
|
14
|
+
super( error )
|
15
|
+
@code = code
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :code
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def connection
|
23
|
+
@@connection ||= Faraday.new( url: 'https://api.parse.com/1/' ) do |faraday|
|
24
|
+
faraday.request :multipart
|
25
|
+
faraday.request :url_encoded
|
26
|
+
faraday.response :logger if Opium.config.log_network_responses
|
27
|
+
faraday.response :json, content_type: /\bjson$/
|
28
|
+
faraday.headers[:x_parse_application_id] = Opium.config.app_id
|
29
|
+
faraday.adapter Faraday.default_adapter
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset_connection!
|
34
|
+
@@connection = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def object_prefix
|
38
|
+
@object_prefix ||= 'classes'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Parse doesn't route User objects through /classes/, instead treating them as a top-level class.
|
42
|
+
def no_object_prefix!
|
43
|
+
@object_prefix = ''
|
44
|
+
end
|
45
|
+
|
46
|
+
def as_resource( name, &block )
|
47
|
+
fail ArgumentError, 'no block given' unless block_given?
|
48
|
+
@masked_resource_name = name.to_s.freeze
|
49
|
+
block.call
|
50
|
+
ensure
|
51
|
+
@masked_resource_name = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def resource_name( resource_id = nil )
|
55
|
+
return @masked_resource_name if @masked_resource_name
|
56
|
+
@resource_name ||= Pathname.new( object_prefix ).join( map_name_to_resource( model_name ) )
|
57
|
+
( resource_id ? @resource_name.join( resource_id ) : @resource_name ).to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def http_get( options = {} )
|
61
|
+
http( :get, options ) do |request|
|
62
|
+
options.fetch(:query, {}).each do |key, value|
|
63
|
+
request.params[key] = key.to_s == 'where' ? value.to_json : value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def http_post( data, options = {} )
|
69
|
+
http( :post, deeply_merge( options, content_type_json ), &infuse_request_with( data ) )
|
70
|
+
end
|
71
|
+
|
72
|
+
def http_put( id, data, options = {} )
|
73
|
+
http( :put, deeply_merge( options, content_type_json, id: id ), &infuse_request_with( data ) )
|
74
|
+
end
|
75
|
+
|
76
|
+
def http_delete( id, options = {} )
|
77
|
+
http( :delete, deeply_merge( options, id: id ) )
|
78
|
+
end
|
79
|
+
|
80
|
+
def requires_heightened_privileges!
|
81
|
+
@requires_heightened_privileges = true
|
82
|
+
end
|
83
|
+
|
84
|
+
def requires_heightened_privileges?
|
85
|
+
!@requires_heightened_privileges.nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def http( method, options, &block )
|
91
|
+
check_for_error( options ) do
|
92
|
+
if options[:sent_headers]
|
93
|
+
applier = apply_headers_to_request( method, options, &block )
|
94
|
+
request = connection.build_request( method )
|
95
|
+
applier.call( request )
|
96
|
+
request.headers
|
97
|
+
else
|
98
|
+
connection.send( method, resource_name( options[:id] ), &apply_headers_to_request( method, options, &block ) )
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def deeply_merge( *args )
|
104
|
+
args.reduce {|a, e| a.deep_merge e }
|
105
|
+
end
|
106
|
+
|
107
|
+
def content_type_json
|
108
|
+
@content_type_json ||= { headers: { content_type: 'application/json' } }
|
109
|
+
end
|
110
|
+
|
111
|
+
def map_name_to_resource( model_name )
|
112
|
+
name = model_name.name.demodulize
|
113
|
+
@object_prefix.empty? ? name.tableize : name
|
114
|
+
end
|
115
|
+
|
116
|
+
def infuse_request_with( data )
|
117
|
+
lambda do |request|
|
118
|
+
request.body = data
|
119
|
+
request.body = request.body.to_json unless request.body.is_a?(String)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def apply_headers_to_request( method, options, &further_operations )
|
124
|
+
lambda do |request|
|
125
|
+
request.headers.update options[:headers] if options[:headers]
|
126
|
+
|
127
|
+
added_master_key =
|
128
|
+
unless request.headers[:x_parse_session_token]
|
129
|
+
if method != :get && requires_heightened_privileges? && Opium.config.master_key
|
130
|
+
request.headers[:x_parse_master_key] = Opium.config.master_key
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
request.headers[:x_parse_rest_api_key] = Opium.config.api_key unless added_master_key
|
135
|
+
|
136
|
+
further_operations.call( request ) if block_given?
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def check_for_error( options = {}, &block )
|
141
|
+
fail ArgumentError, 'no block given' unless block_given?
|
142
|
+
result = yield
|
143
|
+
if options[:raw_response] || options[:sent_headers]
|
144
|
+
result
|
145
|
+
else
|
146
|
+
result = result.body
|
147
|
+
result = result.is_a?(Hash) ? result.with_indifferent_access : {}
|
148
|
+
fail ParseError.new( result[:code], result[:error] ) if result[:code] && result[:error]
|
149
|
+
result
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
class Criteria
|
4
|
+
include Opium::Model::Queryable::ClassMethods
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
class_attribute :models
|
8
|
+
self.models = {}.with_indifferent_access
|
9
|
+
|
10
|
+
def initialize( model_name )
|
11
|
+
@model_name = model_name.respond_to?(:name) ? model_name.name : model_name
|
12
|
+
constraints[:count] = 1
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :model_name
|
16
|
+
|
17
|
+
def model
|
18
|
+
models[model_name] ||= model_name.constantize
|
19
|
+
end
|
20
|
+
|
21
|
+
def chain
|
22
|
+
Marshal.load( Marshal.dump( self ) )
|
23
|
+
end
|
24
|
+
|
25
|
+
def constraints
|
26
|
+
@constraints ||= {}.with_indifferent_access
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_constraint( constraint, value )
|
30
|
+
chain.tap {|c| c.update_constraint!( constraint, value )}
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_constraint!( constraint, value )
|
34
|
+
update_hash_value :constraints, constraint, value
|
35
|
+
end
|
36
|
+
|
37
|
+
def constraints?
|
38
|
+
!constraints.except(:count).empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
def variables
|
42
|
+
@variables ||= {}.with_indifferent_access
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_variable( variable, value )
|
46
|
+
chain.tap {|c| c.update_variable!( variable, value )}
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_variable!( variable, value )
|
50
|
+
update_hash_value :variables, variable, value
|
51
|
+
end
|
52
|
+
|
53
|
+
def variables?
|
54
|
+
!variables.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
def empty?
|
58
|
+
count == 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def criteria
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def ==( other )
|
66
|
+
other.is_a?( self.class ) && self.model_name == other.model_name && self.constraints == other.constraints && self.variables == other.variables
|
67
|
+
end
|
68
|
+
|
69
|
+
def each(&block)
|
70
|
+
if !block_given?
|
71
|
+
to_enum(:each)
|
72
|
+
elsif cached? && @cache
|
73
|
+
@cache.each(&block)
|
74
|
+
else
|
75
|
+
response = self.model.http_get( query: self.constraints )
|
76
|
+
@cache = []
|
77
|
+
if response && response['results']
|
78
|
+
variables[:total_count] = response['count']
|
79
|
+
response['results'].each do |attributes|
|
80
|
+
model = self.model.new( attributes )
|
81
|
+
@cache << model if cached?
|
82
|
+
block.call model
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def inspect
|
89
|
+
inspected_constraints = constraints.map {|k, v| [k, v.inspect].join(': ')}.join(', ')
|
90
|
+
inspected_constraints.prepend ' ' if inspected_constraints.size > 0
|
91
|
+
"#<#{ self.class.name }<#{ model_name }>#{ inspected_constraints }>"
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_parse
|
95
|
+
{}.with_indifferent_access.tap do |result|
|
96
|
+
result[:query] = { where: constraints[:where], className: model_name } if constraints[:where]
|
97
|
+
result[:key] = constraints[:keys] if constraints[:keys]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def uncache
|
102
|
+
super.tap do |criteria|
|
103
|
+
criteria.instance_variable_set(:@cache, nil)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def total_count
|
108
|
+
count && variables[:total_count]
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def update_hash_value( hash_name, key, value )
|
114
|
+
hash = self.send( hash_name )
|
115
|
+
if hash[key].nil? || !value.is_a?(Hash)
|
116
|
+
hash[key] = value
|
117
|
+
elsif hash[key].is_a?(Hash) || value.is_a?(Hash)
|
118
|
+
hash[key].deep_merge!( value )
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Dirty
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveModel::Dirty
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize( attributes = {} )
|
11
|
+
super( attributes ).tap { self.send :clear_changes_information }
|
12
|
+
end
|
13
|
+
|
14
|
+
def save( options = {} )
|
15
|
+
super( options ).tap { self.send :changes_applied }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
unless defined?(clear_changes_information)
|
21
|
+
def clear_changes_information
|
22
|
+
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
23
|
+
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
unless defined?(changes_applied)
|
28
|
+
def changes_applied
|
29
|
+
@previously_changed = changes
|
30
|
+
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
class Field
|
4
|
+
def initialize(name, type, default, readonly, as)
|
5
|
+
self.name, self.type, self.default, self.readonly, self.as = name, type, default, readonly, as
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :name, :type, :readonly, :as
|
9
|
+
|
10
|
+
def default
|
11
|
+
if @default.respond_to? :call
|
12
|
+
@default.call
|
13
|
+
else
|
14
|
+
@default
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def readonly?
|
19
|
+
self.readonly == true
|
20
|
+
end
|
21
|
+
|
22
|
+
def name_to_parse
|
23
|
+
@name_to_parse ||= (self.as || self.name).to_s.camelize(:lower)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_writer :name, :type, :default, :readonly, :as
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'opium/model/field'
|
2
|
+
|
3
|
+
module Opium
|
4
|
+
module Model
|
5
|
+
module Fieldable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
field :id, type: String, readonly: true, as: :object_id
|
10
|
+
field :created_at, type: DateTime, readonly: true
|
11
|
+
field :updated_at, type: DateTime, readonly: true
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def field( name, options = {} )
|
16
|
+
name = name.to_sym
|
17
|
+
fields[name] = Field.new( name, options[:type] || Object, options[:default], options[:readonly] || false, options[:as] )
|
18
|
+
ruby_canonical_field_names[name] = ruby_canonical_field_names[fields[name].name_to_parse] = name.to_s
|
19
|
+
parse_canonical_field_names[name] = parse_canonical_field_names[fields[name].name_to_parse] = fields[name].name_to_parse.to_s
|
20
|
+
class_eval do
|
21
|
+
define_attribute_methods [name]
|
22
|
+
define_method(name) do
|
23
|
+
self.attributes[name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
unless self.respond_to? "#{name}="
|
27
|
+
class_eval do
|
28
|
+
define_method("#{name}=") do |value|
|
29
|
+
converted = self.class.fields[name].type.to_ruby(value)
|
30
|
+
send( "#{name}_will_change!" ) unless self.attributes[name] == converted
|
31
|
+
self.attributes[name] = converted
|
32
|
+
end
|
33
|
+
send(:private, "#{name}=") if options[:readonly]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
fields[name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def fields
|
40
|
+
@fields ||= ActiveSupport::HashWithIndifferentAccess.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def ruby_canonical_field_names
|
44
|
+
@ruby_canonical_field_names ||= ActiveSupport::HashWithIndifferentAccess.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_canonical_field_names
|
48
|
+
@parse_canonical_field_names ||= ActiveSupport::HashWithIndifferentAccess.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def default_attributes
|
52
|
+
ActiveSupport::HashWithIndifferentAccess[ *fields.map {|key, field| [key, field.default]}.flatten ]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Findable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def find( id )
|
8
|
+
new self.http_get( id: id )
|
9
|
+
end
|
10
|
+
|
11
|
+
delegate \
|
12
|
+
:first,
|
13
|
+
:each,
|
14
|
+
:each_with_index,
|
15
|
+
:map,
|
16
|
+
to: :criteria
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
if defined?( Kaminari )
|
2
|
+
module Opium
|
3
|
+
module Model
|
4
|
+
module Kaminari
|
5
|
+
module Queryable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include ::Kaminari::PageScopeMethods
|
10
|
+
|
11
|
+
alias_method :offset, :skip
|
12
|
+
|
13
|
+
delegate :max_per_page, :default_per_page, :max_pages, to: :model_class
|
14
|
+
|
15
|
+
define_method ::Kaminari.config.page_method_name do |num|
|
16
|
+
cache.limit( limit_value ).offset( limit_value * ((num = num.to_i - 1) < 0 ? 0 : num) )
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method :per do |num|
|
20
|
+
super( num ).cache
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def limit_value
|
25
|
+
criteria.constraints.fetch(:limit, default_per_page)
|
26
|
+
end
|
27
|
+
|
28
|
+
def offset_value
|
29
|
+
criteria.constraints.fetch(:skip, 0)
|
30
|
+
end
|
31
|
+
|
32
|
+
def model_class
|
33
|
+
criteria.model
|
34
|
+
end
|
35
|
+
|
36
|
+
def entry_name
|
37
|
+
model_class.model_name.human.downcase
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Opium::Model::Queryable::ClassMethods.send :include, Kaminari::Queryable
|
43
|
+
Opium::Model::Criteria.send :include, Kaminari::Queryable
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Kaminari
|
4
|
+
module Scopable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
include ::Kaminari::ConfigurationMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Opium::Model::Scopable.send :include, Kaminari::Scopable
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Persistable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
end
|
8
|
+
|
9
|
+
class InvalidError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def create( attributes = {} )
|
14
|
+
new( attributes ).tap {|model| model.save}
|
15
|
+
end
|
16
|
+
|
17
|
+
def create!( attributes = {} )
|
18
|
+
new( attributes ).tap {|model| model.save!}
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy_all( query = nil )
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete_all( query = nil )
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def added_headers
|
30
|
+
@added_headers ||= {}.with_indifferent_access
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_header_to( methods, header, value, options = {} )
|
34
|
+
Array( methods ).each do |method|
|
35
|
+
added_headers[method] = { header: header, value: value, options: options }
|
36
|
+
|
37
|
+
added_headers[method][:options][:only] = Array( options[:only] )
|
38
|
+
added_headers[method][:options][:except] = Array( options[:except] )
|
39
|
+
end
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_header_for( method, context, owner = nil )
|
44
|
+
return {} unless added_headers[method]
|
45
|
+
|
46
|
+
eval_only = !added_headers[method][:options][:only].empty?
|
47
|
+
eval_except = !added_headers[method][:options][:except].empty?
|
48
|
+
|
49
|
+
within_only = added_headers[method][:options][:only].include?( context )
|
50
|
+
within_except = added_headers[method][:options][:except].include?( context )
|
51
|
+
|
52
|
+
value = added_headers[method][:value]
|
53
|
+
value = value.call( owner ) if owner && value.respond_to?( :call )
|
54
|
+
|
55
|
+
if value && ( ( !eval_only && !eval_except ) || ( eval_only && within_only ) || ( eval_except && !within_except ) )
|
56
|
+
{ headers: { added_headers[method][:header] => value } }
|
57
|
+
else
|
58
|
+
{}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def save( options = {} )
|
64
|
+
create_or_update( options )
|
65
|
+
rescue => e
|
66
|
+
errors.add( :base, e.to_s )
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
def save!
|
71
|
+
create_or_update( validates: true ) || raise( InvalidError, 'failed to save, as model is invalid' )
|
72
|
+
end
|
73
|
+
|
74
|
+
def update_attributes( attributes = {} )
|
75
|
+
self.attributes = attributes
|
76
|
+
save
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_attributes!( attributes = {} )
|
80
|
+
self.attributes = attributes
|
81
|
+
save!
|
82
|
+
end
|
83
|
+
|
84
|
+
def touch
|
85
|
+
save( validates: false )
|
86
|
+
end
|
87
|
+
|
88
|
+
def delete( options = {} )
|
89
|
+
self.tap do
|
90
|
+
attributes_or_headers( :delete, :delete, options ) do |headers|
|
91
|
+
self.class.http_delete id, headers unless new_record?
|
92
|
+
end
|
93
|
+
self.freeze
|
94
|
+
end
|
95
|
+
end
|
96
|
+
alias_method :destroy, :delete
|
97
|
+
|
98
|
+
def new_record?
|
99
|
+
self.id.nil?
|
100
|
+
end
|
101
|
+
|
102
|
+
def persisted?
|
103
|
+
!new_record? && !self.changed?
|
104
|
+
end
|
105
|
+
|
106
|
+
def pointer
|
107
|
+
@pointer ||= Pointer.new( model: self.class, id: id ) unless new_record?
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_parse
|
111
|
+
pointer.to_parse
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def create_or_update( options )
|
117
|
+
if options[:validates] == false || valid?
|
118
|
+
if new_record?
|
119
|
+
create( options )
|
120
|
+
else
|
121
|
+
update( options )
|
122
|
+
end
|
123
|
+
end.present?
|
124
|
+
end
|
125
|
+
|
126
|
+
def create( options = {} )
|
127
|
+
self.attributes = attributes_or_headers( :post, :create, options ) do |headers|
|
128
|
+
self.class.http_post self.attributes_to_parse( except: [:id, :created_at, :updated_at] ), headers
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def update( options = {} )
|
133
|
+
self.attributes = attributes_or_headers( :put, :update, options ) do |headers|
|
134
|
+
self.class.http_put id, self.attributes_to_parse( only: changes.keys ), headers
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def sent_headers
|
139
|
+
@_sent_headers || {}
|
140
|
+
end
|
141
|
+
|
142
|
+
def attributes_or_headers( method, action, options, &block )
|
143
|
+
result = block.call( options.deep_merge self.class.get_header_for( method, action, self ) )
|
144
|
+
if options[:sent_headers]
|
145
|
+
@_sent_headers = result
|
146
|
+
{}
|
147
|
+
else
|
148
|
+
result
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|