backframe 0.0.0 → 0.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6893ca50993e2054bfa00298833e7d1d95762c6b
4
- data.tar.gz: fcaf54876f10f59cc5e9a8c4306caccfc8e9b5ff
3
+ metadata.gz: b82eb2645a15741b2dfda3c0fea9ae60c1d90ed8
4
+ data.tar.gz: 32f108b39fe6a99f90b936e4d47964cf9b25f2b2
5
5
  SHA512:
6
- metadata.gz: 1b3d72462843957ff8c6c5b8380f7e29d0b7133d99e79bc737527f56e9dbe985d42818c3dfd13fb2304a3bf028da1085ad7ecfe2dcab9fb33259b5e60bcd6e09
7
- data.tar.gz: 68ebc292ef2ea169459348d7c31955976bcdd8432afb30b066ee7d88da46ca9270cb037bfa816ad4997ce7287b52e096772230ee61357bbcb5b2dd752e541743
6
+ metadata.gz: e6176bfbbef7a17bdf19d1cefb6bd55f7b015a7b8dba667b2d89a85677612795c99c63503443f7a9874a2a2a3bc141990fa199d5d7a5db49e4623d47527142aa
7
+ data.tar.gz: 02130ecd29e535126f3714f75d43b78bf6ccd204399fd80c928b56a874f072c793b06bb1a854cdae6e12065cfb5467b22daf393f5dcc4cf14d5944b6243106ef
data/Gemfile CHANGED
@@ -3,7 +3,9 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :development, :test do
6
- gem 'active_model_serializers', '0.10.0.rc4'
6
+ gem 'write_xlsx'
7
+ gem 'active_model_serializers'
8
+ gem 'activerecord'
7
9
  gem 'factory_girl'
8
10
  gem 'faker'
9
11
  gem 'kaminari'
data/backframe.gemspec CHANGED
@@ -16,6 +16,8 @@ Gem::Specification.new do |gem|
16
16
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
17
  gem.require_paths = ["lib"]
18
18
 
19
+ gem.add_runtime_dependency 'write_xlsx', '~> 0.83.0'
20
+ gem.add_runtime_dependency 'activerecord', '~> 4.0'
19
21
  gem.add_runtime_dependency 'activesupport', '~> 4.0'
20
22
  gem.add_runtime_dependency 'active_model_serializers', '>= 0.10.0.rc4'
21
23
  gem.add_runtime_dependency 'kaminari', '~> 0.16'
@@ -0,0 +1,38 @@
1
+ module Backframe
2
+ module ActsAsOrderable
3
+ extend ActiveSupport::Concern
4
+
5
+ def self.included(base)
6
+ base.send :extend, ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def acts_as_orderable(field, args = {})
12
+
13
+ class_eval <<-EOV
14
+ validates_presence_of :#{field}
15
+
16
+ before_validation :set_#{field}, :on => :create
17
+ after_destroy :reorder_#{field}
18
+
19
+ default_scope -> { order(:#{field} => :asc) }
20
+
21
+ def set_#{field}
22
+ self.#{field} ||= (self.#{args[:on]}.#{self.name.tableize}.any?) ? self.#{args[:on]}.#{self.name.tableize}.maximum(:#{field}) + 1 : 0
23
+ end
24
+
25
+ def reorder_#{field}
26
+ self.#{args[:on]}.#{self.name.tableize}.order(:#{field} => :asc).each_with_index do |item, index|
27
+ item.update_column(:#{field}, index)
28
+ end
29
+ end
30
+
31
+ EOV
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,52 @@
1
+ module Backframe
2
+ module ActsAsStatus
3
+ extend ActiveSupport::Concern
4
+
5
+ def self.included(base)
6
+ base.send :extend, ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def acts_as_status(*args)
12
+
13
+ field = args[0]
14
+ arguments = args[1]
15
+
16
+ if arguments.key?(:default)
17
+ after_initialize :init_status, :if => Proc.new { |d| d.new_record? }
18
+ end
19
+
20
+ if arguments.key?(:required)
21
+ validates_presence_of field
22
+ end
23
+
24
+ if arguments.key?(:in)
25
+ validates_inclusion_of field, :in => arguments[:in]
26
+ end
27
+
28
+ if arguments.key?(:in)
29
+ arguments[:in].each do |status|
30
+ class_eval <<-EOV
31
+ scope :#{status}, -> { where(:status => '#{status}') }
32
+
33
+ def #{status}?
34
+ self.#{field} == '#{status}'
35
+ end
36
+ EOV
37
+ end
38
+ end
39
+
40
+ if arguments.key?(:default)
41
+ class_eval <<-EOV
42
+ def init_status
43
+ self.#{field} ||= '#{arguments[:default]}'
44
+ end
45
+ EOV
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,101 @@
1
+ module Backframe
2
+ module ActsAsUser
3
+ extend ActiveSupport::Concern
4
+
5
+ def self.included(base)
6
+ base.send :extend, ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def acts_as_user
12
+
13
+ attr_accessor :password, :change_password, :set_password, :old_password, :new_password, :confirm_password, :confirm_email
14
+
15
+ has_many :activations, :dependent => :destroy
16
+ has_many :resets, :dependent => :destroy
17
+
18
+ after_validation :set_new_password, :if => Proc.new { |u| u.new_password.present? }
19
+ after_create :activate
20
+ after_save :update_activities, :if => Proc.new { |u| u.first_name_changed? || u.last_name_changed? }
21
+
22
+ validates_presence_of :first_name, :last_name, :email
23
+ validates_uniqueness_of :email
24
+ validate :validate_password, :if => Proc.new { |u| u.change_password.present? || u.set_password.present? }
25
+
26
+ class_eval <<-EOV
27
+ def full_name
28
+ self.first_name+' '+self.last_name
29
+ end
30
+
31
+ def rfc822
32
+ self.full_name+' <'+self.email+'>'
33
+ end
34
+
35
+ def locked_out?
36
+ self.signin_locked_at.present? && self.signin_locked_at > Time.now.ago(5.minutes)
37
+ end
38
+
39
+ def signin(status)
40
+ if status == :success
41
+ self.signin_locked_at = nil
42
+ self.signin_attempts = 0
43
+ elsif status == :failed
44
+ self.signin_attempts += 1
45
+ if self.signin_attempts >= 5
46
+ self.signin_attempts = 0
47
+ self.signin_locked_at = Time.now
48
+ end
49
+ end
50
+ self.save
51
+ end
52
+
53
+ def authenticate(password)
54
+ self.password_hash == BCrypt::Engine.hash_secret(password, self.password_salt)
55
+ end
56
+
57
+ def reset
58
+ self.resets.create
59
+ end
60
+
61
+ def activate
62
+ self.activations.create
63
+ end
64
+
65
+ def password=(password)
66
+ @password = password
67
+ self.password_salt = BCrypt::Engine.generate_salt
68
+ self.password_hash = BCrypt::Engine.hash_secret(password, self.password_salt)
69
+ end
70
+
71
+ def password
72
+ @password
73
+ end
74
+
75
+ def validate_password
76
+ if self.change_password && self.old_password.blank?
77
+ return self.errors.add(:old_password, I18n.t('activerecord.errors.messages.blank'))
78
+ elsif self.change_password && !authenticate(self.old_password)
79
+ return self.errors.add(:old_password, I18n.t(:user_invalid_old_password))
80
+ elsif self.new_password.blank? || self.confirm_password.blank?
81
+ return self.errors.add(:new_password, I18n.t(:user_unconfirmed_password))
82
+ elsif self.confirm_password != self.new_password
83
+ return self.errors.add(:new_password, I18n.t(:user_unmatching_passwords))
84
+ end
85
+ end
86
+
87
+ def set_new_password
88
+ self.password = self.new_password
89
+ end
90
+
91
+ def update_activities
92
+ Activity.where(:#{self.name.downcase}_id => self.id).update_all(:updated_at => Time.zone.now)
93
+ end
94
+
95
+ EOV
96
+
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -104,49 +104,44 @@ module Backframe
104
104
  end
105
105
 
106
106
  def collection_to_xls(collection, serializer, fields)
107
- rows = []
108
- rows << '<?xml version="1.0"?>'
109
- rows << '<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">'
110
- rows << '<Worksheet ss:Name="Sheet1">'
111
- rows << '<Table>'
112
- line = []
113
- fields.each do |key|
114
- line << '<Cell><Data ss:Type="String">'+key+'</Data></Cell>'
107
+ filename = SecureRandom.hex(32).to_s.upcase[0,16]
108
+ workbook = WriteXLSX.new(filename)
109
+ worksheet = workbook.add_worksheet
110
+ row = 0
111
+ col = 0
112
+ fields.each_with_index do |key, col|
113
+ worksheet.write(row, col, key)
115
114
  end
116
- rows << "<Row>"+line.join+"</Row>"
117
- collection.all.each do |record|
118
- line = []
119
- serialized = serializer.new(record)
120
- fields.each do |fullkey|
115
+ collection.all.each_with_index do |record, index|
116
+ row = index + 1
117
+ serialized = serializer.new(record).attributes
118
+ fields.each_with_index do |fullkey, col|
121
119
  value = serialized
122
120
  fullkey.to_s.split(".").each do |key|
123
- if value.respond_to?(key)
124
- value = value.send(key)
125
- if value.is_a?(Hash)
126
- value = OpenStruct.new(value)
127
- end
121
+ key = key.to_sym
122
+ if value.is_a?(Hash) && value.key?(key)
123
+ value = value[key]
128
124
  else
129
125
  value = nil
130
126
  end
131
127
  end
132
- if value.is_a?(Numeric)
133
- line << '<Cell><Data ss:Type="Number">'+value+'</Data></Cell>'
134
- elsif value.is_a?(Time)
135
- line << '<Cell><Data ss:Type="String">'+value.strftime("%F %T")+'</Data></Cell>'
128
+ if value.is_a?(Time)
129
+ value = value.strftime("%F %T")
136
130
  elsif value.is_a?(Date)
137
- line << '<Cell><Data ss:Type="String">'+value.strftime("%F")+'</Data></Cell>'
138
- elsif value.is_a?(String)
139
- line << '<Cell><Data ss:Type="String">'+value+'</Data></Cell>'
140
- else
141
- line << '<Cell><Data ss:Type="String"></Data></Cell>'
131
+ value = value.strftime("%F")
132
+ elsif value.is_a?(TrueClass)
133
+ value = 'true'
134
+ elsif value.is_a?(FalseClass)
135
+ value = 'false'
142
136
  end
137
+ value = (!value.is_a?(String)) ? '' : value
138
+ worksheet.write(row, col, value)
143
139
  end
144
- rows << "<Row>"+line.join+"</Row>"
145
140
  end
146
- rows << '</Table>'
147
- rows << '</Worksheet>'
148
- rows << '</Workbook>'
149
- rows.join("\n")
141
+ workbook.close
142
+ data = open(filename).read
143
+ File.unlink(filename)
144
+ data
150
145
  end
151
146
 
152
147
  def pagination_links(collection, per_page, page)
@@ -0,0 +1,56 @@
1
+ module Backframe
2
+ module FilterSort
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+ def filter_query(relation, filters, fields)
7
+ if filters.key?(:q)
8
+ conditions = []
9
+ params = []
10
+ fields.each do |field|
11
+ conditions << "LOWER(#{field}::VARCHAR) LIKE ?"
12
+ params << '%'+filters[:q].downcase+'%'
13
+ end
14
+ args = params.unshift(conditions.join(' OR '))
15
+ relation = relation.where(args)
16
+ end
17
+ relation
18
+ end
19
+
20
+ def filter_range(relation, filters, start_date, end_date = nil)
21
+ end_date ||= start_date
22
+ if filters.key?(:start_date) && filters.key?(:end_date)
23
+ relation = relation.where("#{self.table_name}.#{start_date} <= ? AND #{self.table_name}.#{end_date} >= ?", filters[:end_date], filters[:start_date])
24
+ end
25
+ relation
26
+ end
27
+
28
+ def filter_boolean(relation, filters, field)
29
+ if filters.key?(field)
30
+ relation = relation.where(:field => true) if filters[field] == 1
31
+ relation = relation.where(:field => false) if filters[field] == 0
32
+ end
33
+ relation
34
+ end
35
+
36
+ def sort(relation, key = nil, order = nil)
37
+ self._sort(relation, key, order, 'created_at', 'desc', [self])
38
+ end
39
+
40
+ def _sort(relation, key, order, default_key = 'created_at', default_order = 'desc', included = nil)
41
+ sortfields = {}
42
+ included ||= [self]
43
+ included.each do |model|
44
+ model.columns.each do |column|
45
+ sortkey = (model == self) ? column.name : "#{model.table_name.singularize}.#{column.name}"
46
+ sortfields[sortkey] = "\"#{model.table_name}\".\"#{column.name}\""
47
+ end
48
+ end
49
+
50
+ key = (key.present? && sortfields.has_key?(key)) ? key : default_key
51
+ order = (order.present?) ? order : default_order
52
+ relation.order("#{sortfields[key]} #{order}")
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ module Backframe
2
+ module Migration
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+
7
+ def add_foreign_key_index(from, to, column)
8
+ add_foreign_key(from, to, :column => column)
9
+ add_index(from, column)
10
+ end
11
+
12
+ def remove_foreign_key_index(from, column)
13
+ add_index(from, column)
14
+ add_foreign_key(from, :column => column)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ module Backframe
2
+ class Railtie < ::Rails::Railtie
3
+ initializer 'backframe' do |_app|
4
+ ActionController::Base.send(:include, Backframe::API)
5
+ ActionController::Base.send(:include, Backframe::Resource)
6
+ ActiveRecord::Base.send(:include, Backframe::FilterSort)
7
+ ActiveRecord::Base.send(:include, Backframe::ActsAsOrderable)
8
+ ActiveRecord::Base.send(:include, Backframe::ActsAsUser)
9
+ ActiveRecord::Base.send(:include, Backframe::ActsAsStatus)
10
+ ActiveRecord::Migration.send(:include, Backframe::Migration)
11
+ Backframe::Mime.register_types
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module Backframe
2
- VERSION = '0.0.0'
2
+ VERSION = '0.0.1'
3
3
  end
data/lib/backframe.rb CHANGED
@@ -1,6 +1,12 @@
1
1
  require 'backframe/api'
2
2
  require 'backframe/mime'
3
3
  require 'backframe/resource'
4
+ require 'backframe/filter_sort'
5
+ require 'backframe/acts_as_orderable'
6
+ require 'backframe/acts_as_status'
7
+ require 'backframe/acts_as_user'
8
+ require 'backframe/migration'
9
+ require 'write_xlsx'
4
10
 
5
11
  module Backframe
6
12
  module Exceptions
@@ -8,3 +14,7 @@ module Backframe
8
14
  class Unauthorized < StandardError; end
9
15
  end
10
16
  end
17
+
18
+ if defined? Rails
19
+ require 'backframe/railtie'
20
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backframe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Kops
@@ -11,6 +11,34 @@ bindir: bin
11
11
  cert_chain: []
12
12
  date: 2016-04-28 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: write_xlsx
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.83.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.83.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: activerecord
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '4.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '4.0'
14
42
  - !ruby/object:Gem::Dependency
15
43
  name: activesupport
16
44
  requirement: !ruby/object:Gem::Requirement
@@ -66,12 +94,18 @@ files:
66
94
  - Rakefile
67
95
  - backframe.gemspec
68
96
  - lib/backframe.rb
97
+ - lib/backframe/acts_as_orderable.rb
98
+ - lib/backframe/acts_as_status.rb
99
+ - lib/backframe/acts_as_user.rb
69
100
  - lib/backframe/api.rb
70
101
  - lib/backframe/api/adapter.rb
71
102
  - lib/backframe/api/errors.rb
72
103
  - lib/backframe/api/headers.rb
73
104
  - lib/backframe/api/page.rb
105
+ - lib/backframe/filter_sort.rb
106
+ - lib/backframe/migration.rb
74
107
  - lib/backframe/mime.rb
108
+ - lib/backframe/railtie.rb
75
109
  - lib/backframe/resource.rb
76
110
  - lib/backframe/resource/actions.rb
77
111
  - lib/backframe/version.rb